PageSourceSearch

https://gccriverside.com/Scripts/Bundles/RockUi?v=jf7w_i5sUH-98o-XVpFWrE5Hq1sPRRGgyYFcA4s3tB01

js gccriverside.com collected 2026-09-25 03:18:14 UTC 425,807 bytes, 9,394 lines download raw bytes

1/* Minification failed. Returning unminified contents.
2(823,59-60): run-time error JS1195: Expected expression: >
3(824,70-71): run-time error JS1195: Expected expression: >
4(826,30-31): run-time error JS1195: Expected expression: )
5(828,26-27): run-time error JS1006: Expected ')': ;
6(831,21-22): run-time error JS1002: Syntax error: }
7(833,54-55): run-time error JS1195: Expected expression: >
8(835,25-26): run-time error JS1002: Syntax error: }
9(839,44-45): run-time error JS1195: Expected expression: )
10(839,46-47): run-time error JS1004: Expected ';': {
11(1097,14-15): run-time error JS1195: Expected expression: ,
12(1098,50-51): run-time error JS1004: Expected ';': {
13(1105,58-59): run-time error JS1195: Expected expression: >
14(1105,95-96): run-time error JS1004: Expected ';': )
15(1107,17-18): run-time error JS1002: Syntax error: }
16(1109,37-38): run-time error JS1004: Expected ';': {
17(1113,50-51): run-time error JS1195: Expected expression: >
18(1113,80-81): run-time error JS1004: Expected ';': )
19(1114,19-23): run-time error JS1034: Unmatched 'else'; no 'if' defined: else
20(1121,50-51): run-time error JS1195: Expected expression: >
21(1121,81-82): run-time error JS1004: Expected ';': )
22(1130,39-40): run-time error JS1004: Expected ';': {
23(1184,5-6): run-time error JS1002: Syntax error: }
24(1184,7-8): run-time error JS1197: Too many errors. The file might not be a JavaScript file: )
25(1183,9-23): run-time error JS1018: 'return' statement outside of function: return exports
26(1131,13,1133,61): run-time error JS1018: 'return' statement outside of function: return (s)
27                ? this.before(s).remove()
28                : $("<p>").append(this.eq(0).clone()).html()
29(834,29-71): run-time error JS1018: 'return' statement outside of function: return selectNodeIds.indexOf(node.id) >= 0
30(833,25-52): run-time error JS1018: 'return' statement outside of function: return allNodes.filter(node
31(830,25-68): run-time error JS1018: 'return' statement outside of function: return filterArray(allNodes, selectNodeIds)
32 */
33Sys.Application.add_load(function() {
34    $(function() {
35        $('[data-toggle="tooltip"]').tooltip();
36    });
37
38    stickyTableHeaderInit()
39});
40
41$(document).on('enabledStickiness.stickyTableHeaders', function(event) {
42    $(window).on('resize', function(event) {
43        stickyTableHeaderInit();
44
45        $('.table-responsive').each(stickyRepaint);
46    });
47    var offsetLeft = $(event.target).offset().left;
48    var twidth = $(event.target).parent('.table-responsive').width();
49    $(event.target)
50        .find('.tableFloatingHeaderOriginal')
51        .css({ left: offsetLeft, right: $(window).width() - offsetLeft - twidth, clip: 'rect(0px, ' + (twidth + 2) + 'px, 300px, 0px)' });
52
53    $(event.target)
54        .parent('.table-responsive')
55        .scroll(stickyRepaint);
56});
57
58function stickyTableHeaderInit() {
59    var stickyHeaders = document.querySelector('.js-sticky-headers');
60
61    if (stickyHeaders) {
62        var navbarFixedTop = document.querySelector('.navbar-fixed-top');
63
64        $(stickyHeaders).each(function() {
65            var table = $(this);
66            if (table.parents('.page-fullscreen-capable').length || table.parents('.is-fullscreen').length) {
67                table.stickyTableHeaders({scrollableArea: table.parents('.panel-block')});
68            } else {
69                if ($(navbarFixedTop).length && $(navbarFixedTop).css('position') === 'fixed') {
70                    table.stickyTableHeaders({ fixedOffset: $(navbarFixedTop) });
71                } else {
72                    table.stickyTableHeaders();
73                }
74            }
75        });
76    }
77}
78
79function stickyRepaint() {
80    var twidth = $(this).width();
81    var scroll = $(this).scrollLeft();
82    var offsetLeft = $(this).offset().left;
83    $('.tableFloatingHeaderOriginal').css({
84        left: offsetLeft - scroll,
85        clip: 'rect(0px, ' + (scroll + twidth) + 'px, 300px, ' + scroll + 'px)'
86    });
87}
88;
89(function ($) {
90    window.Rock = window.Rock || {};
91
92    Rock.dialogs = (function () {
93        var _dialogs = {},
94            exports = {
95                // Presents a bootstrap style alert box with the specified message
96                // then executes the callback function(result)
97                alert: function (msg) {
98                    bootbox.dialog({
99                        message: msg,
100                        buttons: {
101                            ok: {
102                                label: 'OK',
103                                className: 'btn-primary'
104                            }
105                        }
106                    });
107                },
108
109                // Presents a bootstrap style alert box with the specified message
110                // then executes the callback function(result)
111                confirm: function (msg, callback) {
112                    bootbox.dialog({
113                        message: msg,
114                        buttons: {
115                            ok: {
116                                label: 'OK',
117                                className: 'btn-primary',
118                                callback: function () {
119                                    callback(true);
120                                }
121                            },
122                            cancel: {
123                                label: 'Cancel',
124                                className: 'btn-default',
125                                callback: function () {
126                                    callback(false);
127                                }
128                            }
129                        }
130                    });
131                },
132
133                // Presents a bootstrap style alert box which prevents on "Cancel" and continues on "Ok"
134                confirmPreventOnCancel: function (e, msg) {
135                    // make sure the element that triggered this event isn't disabled
136                    if (e.currentTarget && e.currentTarget.disabled) {
137                        return false;
138                    }
139
140                    e.preventDefault();
141                    bootbox.dialog({
142                        message: msg,
143                        buttons: {
144                            ok: {
145                                label: 'OK',
146                                className: 'btn-primary',
147                                callback: function () {
148                                    var postbackJs = e.target.href ? e.target.href : e.target.parentElement.href;
149                                    window.location = postbackJs;
150                                }
151                            },
152                            cancel: {
153                                label: 'Cancel',
154                                className: 'btn-default'
155                            }
156                        }
157                    });
158                },
159
160                // Presents a bootstrap style alert box with a 'Are you sure you want to delete this ...' message
161                // Returns true if the user selects OK
162                confirmDelete: function (e, nameText, additionalMsg)
163                {
164                    var msg = 'Are you sure you want to delete this ' + nameText + '?';
165                    if (additionalMsg)
166                    {
167                        msg += ' ' + additionalMsg;
168                    }
169                    this.confirmPreventOnCancel(e, msg);
170                },
171
172                // Updates the modal so that scrolling works
173                updateModalScrollBar: function (controlId) {
174                    Rock.controls.modal.updateSize(controlId);
175                }
176            }
177
178        return exports;
179    }());
180}(jQuery));
181;
182(function () {
183    'use strict';
184    window.Rock = window.Rock || {};
185    Rock.settings = (function () {
186        var _settings = {},
187            exports = {
188                initialize: function (options) {
189                    if (typeof options === 'object') {
190                        _settings = options;
191                    }
192                },
193                get: function (key) {
194                    return _settings[key];
195                },
196                insert: function (key, value) {
197                    _settings[key] = value;
198                },
199                remove: function (key) {
200                    if (_settings[key]) {
201                        delete _settings[key];
202                    }
203                }
204            };
205 
206        return exports;
207    }());
208}());;
209(function () {
210    'use strict';
211    window.Rock = window.Rock || {};
212
213    Rock.utility = (function () {
214        var _utility = {},
215            exports = {
216
217                setContext: function (restController, entityId) {
218                    // Get the current block instance object
219                    $.ajax({
220                        type: 'PUT',
221                        url: Rock.settings.get('baseUrl') + 'api/' + restController + '/SetContext/' + entityId,
222                        success: function (getData, status, xhr) {
223                        },
224                        error: function (xhr, status, error) {
225                            alert(status + ' [' + error + ']: ' + xhr.responseText);
226                        }
227                    });
228                },
229                uuidv4: function () {
230                    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
231                        var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
232                        return v.toString(16);
233                    });
234                },
235                dashify: function (str) {
236                    return str.replace(/\W+/g, '-').replace(/\-$/, '').toLowerCase();
237                }
238            };
239
240        return exports;
241    }());
242}());
243;
244(function ($) {
245    'use strict';
246    window.Rock = window.Rock || {};
247    Rock.controls = Rock.controls || {};
248
249    Rock.controls.accountPicker = (function () {
250        var AccountPicker = function (options) {
251            this.options = options;
252            // set a flag so that the picker only auto-scrolls to a selected item once. This prevents it from scrolling at unwanted times
253            this.alreadyScrolledToSelected = false;
254            this.iScroll = null;
255        },
256            exports;
257
258        AccountPicker.prototype = {
259            constructor: AccountPicker,
260            initialize: function () {
261                var $control = $('#' + this.options.controlId),
262                    $tree = $control.find('.treeview'),
263                    treeOptions = {
264                        customDataItems: this.options.customDataItems,
265                        enhanceForLongLists: this.options.enhanceForLongLists,
266                        multiselect: this.options.allowMultiSelect,
267                        categorySelection: this.options.allowCategorySelection,
268                        categoryPrefix: this.options.categoryPrefix,
269                        restUrl: this.options.restUrl,
270                        searchRestUrl: this.options.searchRestUrl,
271                        restParams: this.options.restParams,
272                        expandedIds: this.options.expandedIds,
273                        expandedCategoryIds: this.options.expandedCategoryIds,
274                        showSelectChildren: this.options.showSelectChildren,
275                        id: this.options.startingId
276                    },
277
278                    // used server-side on postback to display the selected nodes
279                    $hfItemIds = $control.find('.js-item-id-value'),
280                    // used server-side on postback to display the expanded nodes
281                    $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'),
282                    $hfExpandedCategoryIds = $control.find('.js-expanded-category-ids');
283
284                // Custom mapping override to add features items
285                this.options.mapItems = function (arr, treeView) {
286
287                    return $.map(arr, function (item) {
288
289                        var node = {
290                            id: item.Guid || item.Id,
291                            name: item.Name || item.Title,
292                            iconCssClass: item.IconCssClass,
293                            parentId: item.ParentId,
294                            hasChildren: item.HasChildren,
295                            isActive: item.IsActive,
296                            countInfo: item.CountInfo,
297                            isCategory: item.IsCategory,
298                            path: item.Path,
299                            totalCount: item.TotalCount
300                        };
301
302                        // Custom node properties passed in from the *Picker.cs using the ItemPicker base CustomDataItems property
303                        if (treeView.options.customDataItems && treeView.options.customDataItems.length > 0) {
304                            treeView.options.customDataItems.forEac
304h(function (dataItem, idx) {
305                                if (!node.hasOwnProperty(dataItem)) {
306                                    node['' + dataItem.itemKey + ''] = item['' + dataItem.itemValueKey + ''];
307                                }
308                            });
309                        }
310
311                        if (node.parentId === null) {
312                            node.parentId = '0';
313                        }
314
315                        if (item.Children && typeof item.Children.length === 'number') {
316
317                            // traverse using the _mapArrayDefault in rockTree.js
318                            node.children = _mapArrayDefault(item.Children, treeView);
319                        }
320
321                        if (node.isCategory) {
322                            node.id = treeView.options.categoryPrefix + node.id;
323                        }
324
325                        return node;
326                    });
327                }
328
329                if (typeof this.options.mapItems === 'function') {
330                    treeOptions.mapping = {
331                        mapData: this.options.mapItems
332                    };
333                }
334
335                // clean up the tree (in case it was initialized already, but we are rebuilding it)
336                var rockTree = $tree.data('rockTree');
337
338                if (rockTree) {
339                    rockTree.nodes = [];
340                }
341                $tree.empty();
342
343                var $scrollContainer = $control.find('.scroll-container .viewport');
344                var $scrollIndicator = $control.find('.track');
345                this.iScroll = new IScroll($scrollContainer[0], {
346                    mouseWheel: true,
347                    indicators: {
348                        el: $scrollIndicator[0],
349                        interactive: true,
350                        resize: false,
351                        listenY: true,
352                        listenX: false,
353                    },
354                    click: false,
355                    preventDefaultException: { tagName: /.*/ }
356                });
357
358                // Since some handlers are "live" events, they need to be bound before tree is initialized
359                this.initializeEventHandlers();
360
361                if ($hfItemIds.val() && $hfItemIds.val() !== '0') {
362                    treeOptions.selectedIds = $hfItemIds.val().split(',');
363                }
364
365                if ($hfExpandedIds.val()) {
366                    treeOptions.expandedIds = $hfExpandedIds.val().split(',');
367                }
368
369                if ($hfExpandedCategoryIds.val()) {
370                    treeOptions.expandedCategoryIds = $hfExpandedCategoryIds.val().split(',');
371                }
372
373                // Initialize the rockTree and pass the tree options also makes http fetches
374                $tree.rockTree(treeOptions);
375
376                $control.find('.picker-preview').hide();
377                $control.find('.picker-treeview').hide();
378
379                if (treeOptions.allowMultiSelect) {
380                    $control.find('.picker-preview').remove();
381                    $control.find('.picker-treeview').remove();
382                }
383
384                this.updateScrollbar();
385            },
386            initializeEventHandlers: function () {
387                var self = this,
388                    $control = $('#' + this.options.controlId),
389                    $spanNames = $control.find('.selected-names'),
390                    $hfItemIds = $control.find('.js-item-id-value'),
391                    $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'),
392                    $hfItemNames = $control.find('.js-item-name-value'),
393                    $searchValueField = $control.find('.js-existing-search-value');
394
395                // Bind tree events
396                $control.find('.treeview')
397                    .on('rockTree:selected', function (e) {
398                        var rockTree = $control.find('.treeview').data('rockTree');
399
400                        if (rockTree.selectedNodes) {
401                            var ids = rockTree.selectedNodes.map(function (v) { return v.id }).join(',');
402                            $hfItemIds.val(ids);
403                        }
404
405                        self.togglePickerElements();
406                    })
407                    .on('rockTree:itemClicked', function (e, data) {
408                        // make sure it doesn't auto-scroll after something has been manually clicked
409                        self.alreadyScrolledToSelected = true;
410                        if (!self.options.allowMultiSelect) {
411                            $control.find('.picker-btn').trigger('click');
412                        }
413                    })
414                    .on('rockTree:expand rockTree:collapse rockTree:dataBound', function (evt, data) {
415                        self.updateScrollbar();
416                        // Get any node item so we can read to total count
417                        const rockTree = $control.find('.treeview').data('rockTree');
418                        const firstNode = rockTree.nodes[0];
419                        if (firstNode && firstNode.totalCount > 1000) {
420                            $control.find('.js-select-all').hide();
421                        }
422
423                        if (self.selectAll) {
424                            self.toggleSelectAll(rockTree);
425                        }
426                    })
427                    .on('rockTree:rendered', function (evt, data) {
428                        var rockTree = $control.find('.treeview').data('rockTree');
429                        self.createSearchControl();
430                        self.showActiveMenu();
431                        self.scrollToSelectedItem();
432
433                        if ($hfItemIds && $hfItemIds.val().length > 0) {
434                            var itemIds = $hfItemIds.val().split(',');
435
436                            rockTree.setSelected(itemIds);
437                        }
438
439                        self.togglePickerElements();
440
441                        if (self.getViewMode() === 'selecting') {
442                            self.setViewMode('clear');
443                            $control.find('.picker-btn').trigger('click', [$hfItemIds.val()]);
444                        }
445
446                        if (self.getViewMode() === 'selected') {
447                            if ($hfItemIds.val() === '0' && !$control.find('.picker-menu').is(':visible') ) {
448                                return;
449                            }
450
451                            $control.find('.picker-cancel').trigger('click');
452                        }
453                    })
454                    .on('rockTree:fetchCompleted', function (evt, data) {
455                        // intentionally empty
456                    })
457
458                $control.find('.picker-label').on('click', function (e) {
459                    e.preventDefault();
460                    $(this).toggleClass("active");
461                    $control.find('.picker-menu').first().toggle(0, function () {
462                        self.scrollToSelectedItem();
463                        $control.find('.js-search-panel input').trigger('focus');
464                    });
465                });
466
467                $control.find('.picker-cancel').on('click', function () {
468                    $(this).toggleClass("active");
469                    $control.find('.picker-menu').hide(0, function () {
470                        self.updateScrollbar();
471                    });
472                    $(this).closest('.picker-label').toggleClass("active");
473
474                    //cleanup
475                    self.setViewMode('clear');
476                    self.setActiveMenu(false);
477                    //doPostBack();
478                });
479
480                // Preview Selection link click
481                $control.find('.picker-preview').on('click', function () {
482                    $control.find('.js-search-panel').hide();
483                    $control.find('.picker-preview').hide();
484                    $control.find('.picker-treeview').show();
485                    $control.find('.js-select-all').hide();
486
487                    self.setViewMode('preview');
488                    self.togglePickerElements();
489
490                    var rockTree = $control.find('.treeview').data('rockTree');
491
492                    // Get all of the current rendered items so we can revert if user leaves search
493                    self.currentTreeView = $control.find('.treeview').html();
494
495                    var $viewport = $control.find('.viewport');
496
497                    if (rockTree.selectedNodes && rockTree.selectedNodes.length > 0) {
498
499                        var listHtml = '';
500                        rockTree.selectedNodes.forEach(function (node) {
501                            if (!node.path) {
502                                node.path = 'Top-Level';
503                            }
504
505                            listHtml +=
506                                '<div id="preview-item-' + node.id + '" class="d-flex align-items-center preview-item js-preview-item">' +
507                                '         <div class="flex-fill">' +
508                                '              <span class="text-color d-block">' + node.name + '</span>' +
509                                '              <span class="text-muted text-sm">' + node.path.replaceAll('^', '<i class="ti ti-chevron-right pl-1 pr-1" aria-hidden="true"></i>') + '</span>' +
510                                '         </div>' +
511                                '         <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="ti ti-x"></i></a>' +
512                                '</div>';
513                        });
514
515                        // Display preview list
516                        var listHtmlView = '<div>' +
517                            listHtml +
518                            '</div>';
519
520                        $control.find('.scroll-container').addClass('scroll-container-native').html(listHtmlView);
521                        // Wire up remove event and remove from dataset
522
523                        $control.find('.js-remove-preview').on('click', function (e) {
524                            var nodeId = $(this).attr('data-id');
525
526                            $control.find('#preview-item-' + nodeId).remove();
527
528                            var newSelected = rockTree.selectedNodes.filter(function (fNode) {
529                                return fNode.id !== nodeId;
530                            });
531
532                            rockTree.selectedNodes = newSelected;
533
534                            // After removing a selection we need to update the hf value
535                            var newIds = newSelected.map(function (n) { return n.id });
536
537                            if (newIds && newIds.length > 0) {
538                                $hfItemIds.val(newIds.join(','));
539                            }
540                            else {
541                                $hfItemIds.val('0');
542                            }
543
544                            if ($control.find('.js-preview-item').length === 0) {
545                                $control.find('.picker-treeview').trigger('click');
546                            }
547                        });
548                    }
549                });
550
551                // Tree View link click
552                $control.find('.picker-treeview').on('click', function () {
553                    $searchValueField.val('');
554                    self.setActiveMenu(true);
555                    self.setViewMode('clear');
556
557                    if ($hfItemIds && $hfItemIds.length > 0) {
558                        var restUrl = self.options.getParentIdsUrl
559                        var nodeIds = $hfItemIds.val().split(',');
560                        var restUrlParams = nodeIds.map(function (id) { return 'ids=' + id }).join('&');
561
562                        restUrl = restUrl + '?' + restUrlParams;
563
564                        // Get the ancestor ids so the tree will expand
565                        $.getJSON(restUrl, function (data, status) {
566                            if (data && status === 'success') {
567                                var selectedIds = [];
568                                var expandedIds = [];
569
570                                $.each(data, function (key, value) {
571                                    selectedIds.push(key);
572
573                                    value.forEach(function (kval) {
574                                        if (!expandedIds.find(function (expVal) {
575                                            return expVal === kval
576                                        })) {
577                                            expandedIds.push(kval);
578                                        }
579                                    });
580                                });
581
582                                if (expandedIds && expandedIds.length > 0) {
583                                    $hfExpandedIds.val(expandedIds.join(','));
584                                }
585
586                                if (selectedIds && selectedIds.length > 0) {
587                                    $hfItemIds.val(selectedIds.join(','));
588                                }
589
590                                doPostBack();
591                            }
592                        });
593                    }
594
595                });
596
597                // have the X appear on hover if something is selected
598                if ($hfItemIds.val() && $hfItemIds.val() !== '0') {
599                    $control.find('.picker-select-none').addClass('rollover-item');
600                    $control.find('.picker-select-none').show();
601                }
602
603                // [Select] button click
604                $control.find('.picker-btn').on('click', function (el) {
605
606                    if (self.getViewMode() === 'preview' || self.getViewMode() === 'search') {
607                        self.setViewMode('selecting');
608                        $('#tbSearch_' + self.options.controlId).val('');
609                        $control.find('.js-existing-search-value').val('');
610
611                        if ($hfItemIds && $hfItemIds.length > 0) {
612                            var restUrl = self.options.getParentIdsUrl
613                            var nodeIds = $hfItemIds.val().split(',');
614                            var restUrlParams = nodeIds.map(function (id) { return 'ids=' + id }).join('&');
615
616                            restUrl = restUrl + '?' + restUrlParams;
617
618                            // Get the ancestor ids so the tree will expand
619                            $.getJSON(restUrl, function (data, status) {
620                                if (data && status === 'success') {
621                                    var selectedIds = [];
622                                    var expandedIds = [];
623
624                                    $.each(data, function (key, value) {
625                                        selectedIds.push(key);
626
627                                        value.forEach(function (kval) {
628                                            if (!expandedIds.find(function (expVal) {
629                                                return expVal === kval
630                                            })) {
631                                                expandedIds.push(kval);
632                                            }
633                                        });
634                                    });
635
636                                    if (expandedIds && expandedIds.length > 0) {
637                                        $hfExpandedIds.val(expandedIds.join(','));
638                                    }
639
640                                    if (selectedIds && selectedIds.length > 0) {
641                                        $hfItemIds.val(selectedIds.join(','));
642                                    }
643
644                                    doPostBack();
645                                }
646                            });
647                        }
648
649                        return;
650                    }
651
652                    var rockTree = $control.find('.treeview').data('rockTree'),
653                        selectedNodes = rockTree.selectedNodes,
654                        selectedIds = [],
655                        selectedNames = [];
656
657                    $.each(selectedNodes, function (index, node) {
658                        var nodeName = $("<textarea/>").html(node.name).text();
659                        selectedNames.push(nodeName);
660                        if (!selectedIds.includes(node.id)) {
661                            selectedIds.push(node.id);
662                        }
663                    });
664
665                    // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
666                    $hfItemIds.val(selectedIds.join(',')).trigger('change');
667                    $hfItemNames.val(selectedNames.join(','));
668
669                    // have the X appear on hover. something is selected
670                    $control.find('.picker-select-none').addClass('rollover-item');
671                    $control.find('.picker-select-none').show();
672
673                    $spanNames.text(selectedNames.join(', '));
674                    $spanNames.attr('title', $spanNames.text());
675
676                    $control.find('.picker-label').toggleClass("active");
677                    $control.find('.picker-menu').hide(0, function () {
678                        self.updateScrollbar();
679                    });
680
681                    self.setViewMode('selected');
682
683                    if (!(el && el.originalEvent && el.originalEvent.srcElement === this)) {
684                        // if this event was called by something other than the button itself, make sure the execute the href (which is probably javascript)
685                        var jsPostback = $(this).attr('href');
686                        if (jsPostback) {
687                            window.location = jsPostback;
688                        }
689                    }
690                });
691
692                $control.find('.picker-select-none').on("click", function (e) {
693                    e.preventDefault();
694                    e.stopImmediatePropagation();
695
696                    $hfItemIds.val('0').trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
697                    $hfItemNames.val('');
698
699                    var rockTree = $control.find('.treeview').data('rockTree');
700                    rockTree.clear();
701
702                    // don't have the X appear on hover. nothing is selected
703                    $control.find('.picker-select-none').removeClass('rollover-item').hide();
704
705                    $control.siblings('.js-hide-on-select-none').hide();
706
707                    $spanNames.text(self.options.defaultText);
708                    $spanNames.attr('title', $spanNames.text());
709                    doPostBack();
710                });
711
712                // clicking on the 'select all' btn
713                $control.on('click', '.js-select-all', function (e){
714                    const $tree = $control.find('.treeview');
715                    const rockTree = $tree.data('rockTree');
716                    self.selectAll = !self.selectAll;
717
718                    e.preventDefault();
719                    e.stopPropagation();
720
721                    let isChildrenLoaded = true;
722                    for (const node of rockTree.nodes) {
723                        if (node.hasChildren && !node.children) {
724                            isChildrenLoaded = false;
725                        }
726                    }
727
728                    if (!isChildrenLoaded) {
729                        rockTree.nodes = [];
730                        rockTree.render();
731                        let treeOptions = {
732                            customDataItems: self.options.customDataItems,
733                            enhanceForLongLists: self.options.enhanceForLongLists,
734                            multiselect: self.options.allowMultiSelect,
735                            categorySelection: self.options.allowCategorySelection,
736                            categoryPrefix: self.options.categoryPrefix,
737                            restUrl: self.options.restUrl,
738                            searchRestUrl: self.options.searchRestUrl,
739                            restParams: self.options.restParams + '&loadChildren=true',
740                            expandedIds: self.options.expandedIds,
741                            expandedCategoryIds: self.options.expandedCategoryIds,
742                            showSelectChildren: self.options.showSelectChildren,
743                            id: self.options.startingId
744                        };
745                        $tree.rockTree(treeOptions);
746                    } else {
747                        self.toggleSelectAll(rockTree);
748                    }
749                });
750            },
751            updateScrollbar: function (sPosition) {
752                var self = this;
753                // first, update this control's scrollbar, then the modal's
754                var $container = $('#' + this.options.controlId).find('.scroll-container');
755
756                if ($container.is(':visible')) {
757                    if (!sPosition) {
758                        sPosition = 'relative'
759                    }
760                    if (self.iScroll) {
761                        self.iScroll.refresh();
762                    }
763                }
764
765                // update the outer modal
766                Rock.dialogs.updateModalScrollBar(this.options.controlId);
767            },
768            scrollToSelectedItem: function () {
769                var $selectedItem = $('#' + this.options.controlId + ' [class^="picker-menu"]').find('.selected').first();
770                if ($selectedItem.length && (!this.alreadyScrolledToSelected)) {
771                    this.updateScrollbar();
772                    this.iScroll.scrollToElement('.selected', '0s');
773                    this.alreadyScrolledToSelected = true;
774                } else {
775                    // initialize/update the scrollbar
776                    this.updateScrollbar();
777                }
778            },
779            showActiveMenu: function () {
780                var self = this;
781                var $control = $('#' + this.options.controlId);
782
783                if (self.isMenuActive()) {
784                    $control.find('.picker-label').click();
785                }
786                self.setActiveMenu(false);
787            },
788            setActiveMenu: function (value) {
789                var $control = $('#' + this.options.controlId);
790                if (value === undefined || value === null) {
791                    value = true;
792                }
793                $control.find('.js-picker-showactive-value').val(value.toString());
794            },
795            isMenuActive: function () {
796                var $control = $('#' + this.options.controlId);
797                var showPickerActive = $control.find('.js-picker-showactive-value').val();
798                var isActive = showPickerActive && showPickerActive === 'true' ? true : false;
799                return isActive;
800            },
801            setViewMode: function (mode) {
802                var $control = $('#' + this.options.controlId);
803                var $hfViewMode = $control.find('.js-picker-view-mode');
804
805                var clear = mode.toLowerCase() === 'clear';
806
807                if (!clear && clear === false) {
808                    $hfViewMode.val(mode);
809                }
810
811                if (clear && clear === true) {
812                    $hfViewMode.val('');
813                }
814            },
815            getViewMode: function (mode) {
816                var $control = $('#' + this.options.controlId);
817                var $hfViewMode = $control.find('.js-picker-view-mode');
818
819                return $hfViewMode.val().toLowerCase();
820            },
821            togglePickerElements: function () {
822                var $control = $('#' + this.options.controlId);
823                var rockTree = $control.find('.treeview').data('rockTree');
824
825                var hasSelected = rockTree.selectedNodes && rockTree.selectedNodes.length > 0;
826
827                switch (this.getViewMode()) {
828                    case 'preview': {
829                        $control.find('.picker-treeview').show();
830                        $control.find('.picker-preview').hide();
831                        $control.find('.js-picker-show-inactive').hide();
832                    }
833                        break;
834                    case 'search': {
835                        $control.find('.picker-treeview').show();
836                        $control.find('.picker-preview').hide();
837                        $control.find('.js-select-all').hide();
838                    }
839                        break;
840                    default: {
841                        if (!hasSelected) {
842                            $control.find('.picker-preview').hide();
843                            $control.find('.picker-treeview').hide();
844                        }
845                        else {
846                            $control.find('.picker-preview').show();
847                        }
848                    }
849
850                }
851            },
852            findNodes: function (allNodes, selectNodeIds) {
853                if (selectNodeIds) {
854                    if ($.isArray(selectNodeIds)) {
855                        const filterArray = (nodes, ids) => {
856                            const filteredNodes = nodes.filter(node => {
857                                return ids.indexOf(node.id) >= 0;
858                            });
859                            return filteredNodes;
860                        };
861
862                        return filterArray(allNodes, selectNodeIds);
863                    }
864                    else {
865                        return allNodes.filter(node => {
866                            return selectNodeIds.indexOf(node.id) >= 0;
867                        });
868                    }
869                }
870            },
871            createSearchControl: function () {
872                var self = this;
873                var controlId = self.options.controlId;
874
875                var $control = $('#' + controlId);
876
877                var rockTree = $control.find('.treeview').data('rockTree');
878
879                if (self.options.enhanceForLongLists === true) {
880
881                    // A hidden value to store the current search criteria to be read on post-backs
882                    var $searchValueField = $control.find('.js-existing-search-value');
883
884                    var $searchControl =
885                        $('	<div id="pnlSearch_' + controlId + '" class="input-group input-group-sm js-search-panel mb-2" > ' +
886                            '		<input id="tbSearch_' + controlId + '" type="text" placeholder="Quick Find" class="form-control" autocapitalize="off" autocomplete="off" autocorrect="off" spellcheck="false" />' +
887                            '		<span class="input-group-btn">' +
888                            '			<a id="btnSearch_' + controlId + '" class="btn btn-default btn-sm"><i class="ti ti-search"></i></a>' +
889                            '		</span>' +
890                            '	</div>');
891
892                    // Get all of the current rendered items so we can revert if user leaves search
893                    var $pickerMenu = $control.find('.picker-menu');
894                    var $treeView = $control.find('.treeview');
895                    var $overview = $control.find('.overview');
896                    var $hfItemIds = $control.find('.js-item-id-value');
897
898                    // Added this check to prevent rendering call from duping the element
899                    if ($pickerMenu.find('.js-search-panel').length === 0) {
900                        // Add the search control after rendering
901                        $pickerMenu.prepend($searchControl);
902                    }
903
904                    var $searchInputControl = $('#tbSearch_' + controlId);
905
906                    $('#btnSearch_' + controlId).off('click').on('click', function () {
907
908                        var currentSelectedNodeIds = rockTree.selectedNodes.map(function (v) { return v.id; });
909
910                        $hfItemIds = $control.find('.js-item-id-value');
911
912                        var searchKeyword = $searchInputControl.val();
913
914                        if (searchKeyword && searchKeyword.length > 0) {
915
916                            self.setViewMode('search');
917
918                            var searchRestUrl = self.options.searchRestUrl;
919                            var restUrlParams = self.options.restParam
919s + '&searchTerm=' + searchKeyword;
920
921                            searchRestUrl += restUrlParams;
922
923                            $.getJSON(searchRestUrl, function (data, status) {
924
925                                if (data && status === 'success') {
926                                    $treeView.html('');
927                                }
928                                else {
929                                    $overview.html(treeView);
930                                    return;
931                                }
932
933                                // Create the search results node object
934                                var nodes = [];
935                                for (var i = 0; i < data.length; i++) {
936                                    var obj = data[i];
937                                    var node = {
938                                        id: obj.Id,
939                                        parentId: obj.ParentId,
940                                        glcode: obj.GlCode,
941                                        title: obj.Name + (obj.GlCode ? ' (' + obj.GlCode + ')' : ''),
942                                        name: obj.Name,
943                                        hasChildren: obj.HasChildren,
944                                        isActive: obj.IsActive,
945                                        path: obj.Path
946                                    };
947
948                                    nodes.push(node);
949                                }
950
951                                if (nodes) {
952
953                                    var listHtml = '';
954                                    nodes.forEach(function (node, idx) {
955
956                                        var disabledCheck = '';
957                                        var mutedText = '';
958                                        if (!node.isActive || node.isActive === false) {
959                                            disabledCheck = ' disabled';
960                                            mutedText = ' text-muted';
961                                        }
962
963                                        var inputHtml = '<input type="radio" data-id="' + node.id + '" class="checkbox js-opt-search"' + disabledCheck + '>';
964                                        var inputType = 'radio';
965                                        if (self.options.allowMultiSelect) {
966                                            inputHtml = '<input type="checkbox" data-id="' + node.id + '" class="checkbox js-chk-search"' + disabledCheck + '>';
967                                            inputType = 'checkbox';
968                                        }
969
970                                        if (node.path === '') {
971                                            node.path = "Top-Level";
972                                        }
973
974                                        listHtml +=
975
976                                            '<div id="divSearchItem-' + node.id + '" class="' + inputType + ' search-item js-search-item">' +
977                                            '      <label>' +
978                                            inputHtml +
979                                            '        <span class="label-text">' +
980                                            '              <span class="text-color d-block' + mutedText + '">' + node.title + '</span>' +
981                                            '              <span class="text-muted text-sm">' + node.path.replaceAll('^', '<i class="ti ti-chevron-right pl-1 pr-1" aria-hidden="true"></i>') + '</span>' +
982                                            '        </span>' +
983                                            '     </label>' +
984                                            '</div>';
985                                    });
986
987                                    // add the results to the panel
988                                    $treeView.html(listHtml);
989
990                                    self.updateScrollbar();
991
992                                    $control.find('.js-chk-search').off('change').on('change', function () {
993                                        var itemIds = [];
994                                        var $allChecked = $control.find('.js-chk-search:checked');
995                                        var checkedVals = $allChecked.map(function () {
996                                            return $(this).attr('data-id');
997                                        }).get();
998
999                                        if (checkedVals && checkedVals.length > 0) {
1000                                            $control.find('.picker-treeview').show();
1001                                            var checkedNodes = self.findNodes(nodes, checkedVals);
1002
1003                                            if (checkedNodes && checkedNodes.length) {
1004                                                checkedNodes.forEac
1004h(function (n) {
1005                                                    if (n.length === 0) { return; }
1006                                                    if (!itemIds.find(function (i) { return n.id === i; })) {
1007                                                        itemIds.push(n.id);
1008                                                    }
1009
1010                                                });
1011                                            }
1012                                        }
1013
1014                                        // We need to reselect any selection that were set in the tree but not part of the search results
1015                                        if (currentSelectedNodeIds && currentSelectedNodeIds.length > 0) {
1016                                            nodes.forEach(function (node) {
1017                                                currentSelectedNodeIds = currentSelectedNodeIds.filter(function (current) { return current !== node.id });
1018                                            });
1019
1020                                            if (currentSelectedNodeIds && currentSelectedNodeIds.length > 0) {
1021                                                itemIds.push(currentSelectedNodeIds);
1022                                            }
1023                                        }
1024
1025                                        if (itemIds && itemIds.length) {
1026                                            // set the selected items on the server control to handle on postback
1027                                            $hfItemIds.val(itemIds.join(','));
1028                                        }
1029                                    });
1030
1031                                    // Handle multi item check selection
1032                                    $control.find('.js-search-item').off('click').on('click', function (e) {
1033
1034                                        var $chkBox = $(this).find('.js-chk-search').first();
1035                                        if ($chkBox.length > 0) {
1036                                            if (!$chkBox.prop('checked')) {
1037                                                $chkBox.prop('checked', true);
1038                                                $control.find('.js-chk-search').trigger('change');
1039                                            }
1040                                            else {
1041                                                $chkBox.prop('checked', false);
1042                                            }
1043                                        }
1044                                    });
1045
1046
1047                                    // Handle single item radio selection
1048                                    $control.find('.js-opt-search').off('change').on('change', function () {
1049                                        var thisNodeId = $(this).attr('data-id');
1050                                        var itemIds = [];
1051                                        //prevent multi select
1052                                        $control.find('.js-opt-search:not([data-id=' + thisNodeId + '])').prop('checked', false);
1053
1054                                        var $allChecked = $control.find('.js-opt-search:checked');
1055                                        var checkedVals = $allChecked.map(function () {
1056                                            return $(this).attr('data-id');
1057                                        }).get();
1058
1059                                        if (checkedVals && checkedVals.length > 0) {
1060                                            var checkedNodes = self.findNodes(nodes, checkedVals);
1061
1062                                            if (checkedNodes && checkedNodes.length) {
1063                                                checkedNodes.forEach(function (n) {
1064                                                    if (n.length === 0) { return; }
1065                                                    if (!itemIds.find(function (i) { return n.id === i; })) {
1066                                                        itemIds.push(n.id);
1067                                                    }
1068                                                });
1069                                            }
1070                                        }
1071
1072                                        if (itemIds && itemIds.length) {
1073                                            // set the selected items on the server control to handle on postback
1074                                            $hfItemIds.val(itemIds.join(','));
1075                                        }
1076                                    });
1077                                }
1078
1079                                //select current selected nodes in search
1080                                if (rockTree.selectedNodes && rockTree.selectedNodes.length) {
1081                                    rockTree.selectedNodes.forEac
1081h(function (selectedNode) {
1082                                        if (self.options.allowMultiSelect && self.options.allowMultiSelect === true) {
1083                                            $control.find('[data-id=' + selectedNode.id + '].js-chk-search').prop('checked', true);
1084                                            var checkedNode = selectedNode;
1085                                            checkedNode.id = selectedNode.id;
1086                                            $control.find('[data-id=' + selectedNode.id + '].js-chk-search').trigger('change');
1087                                        }
1088                                        else {
1089                                            $control.find('[data-id=' + selectedNode.id + '].js-opt-search').prop('checked', true);
1090                                            var enabledNode = selectedNode;
1091                                            enabledNode.id = selectedNode.id;
1092                                            $control.find('[data-id=' + selectedNode.id + '].js-opt-search').trigger('change');
1093                                        }
1094                                    });
1095                                }
1096                            });
1097                        }
1098
1099                    });
1100
1101                    // If we have an existing search value on postback
1102                    if ($searchValueField.length > 0 && $searchValueField.val().length > 0) {
1103                        $searchInputControl.val($searchValueField.val());
1104                        $('#btnSearch_' + controlId).click();
1105                    }
1106
1107                    // Handle the input searching
1108                    $searchInputControl.keyup(function (keyEvent) {
1109                        keyEvent.preventDefault();
1110
1111                        var searchKeyword = $searchInputControl.val();
1112
1113                        if (!searchKeyword || searchKeyword.length === 0) {
1114                            $control.find('.picker-treeview').trigger('click');
1115                        }
1116
1117                        self.togglePickerElements();
1118                    }).keydown(function (keyEvent) {
1119
1120                        var searchKeyword = $searchInputControl.val();
1121                        $searchValueField.val(searchKeyword);
1122
1123                        if (keyEvent.which === 13) {
1124                            keyEvent.preventDefault();
1125                            $('#btnSearch_' + controlId).click();
1126                        }
1127                    });
1128                }
1129            },
1130            toggleSelectAll: function (rockTree) {
1131
1132                const $itemNameNodes = rockTree.$el.find('.rocktree-name');
1133
1134                const setNodeOpenState = function (node, isOpen) {
1135                    if (node.hasChildren && node.children) {
1136                        node.isOpen = isOpen;
1137                        node.children.forEach(childNode => setNodeOpenState(childNode, isOpen));
1138                    }
1139                }
1140
1141                if (this.selectAll) {
1142                    // mark them all as unselected (just in case some are selected already), then click them to select them
1143                    $itemNameNodes.removeClass('selected');
1144                    $itemNameNodes.trigger('click');
1145                    rockTree.nodes.forEach(node => setNodeOpenState(node, true));
1146                } else {
1147                    // if all were already selected, toggle them to unselected
1148                    rockTree.setSelected([]);
1149                    const $control = $('#' + this.options.controlId);
1150                    const $hfItemIds = $control.find('.js-item-id-value');
1151                    $hfItemIds.val('0');
1152                    $itemNameNodes.removeClass('selected');
1153                    rockTree.nodes.forEach(node => setNodeOpenState(node, false));
1154                }
1155
1156                rockTree.render();
1157            },
1158            selectAll: false
1159        }
1160
1161        // jquery function to ensure HTML is state remains the same each time it is executed
1162        $.fn.outerHTML = function (s) {
1163            return (s)
1164                ? this.before(s).remove()
1165                : $("<p>").append(this.eq(0).clone()).html();
1166        }
1167
1168        exports = {
1169            defaults: {
1170                id: 0,
1171                controlId: null,
1172                restUrl: null,
1173                searchRestUrl: null,
1174                restParams: null,
1175                allowCategorySelection: false,
1176                categoryPrefix: '',
1177                allowMultiSelect: false,
1178                defaultText: '',
1179                selectedIds: null,
1180                expandedIds: null,
1181                expandedCategoryIds: null,
1182                showSelectChildren: false,
1183                enhanceForLongLists: false,
1184                customDataItems: []
1185            },
1186            controls: {},
1187            initialize: function (options) {
1188                var settings,
1189                    accountPicker;
1190
1191                if (!options.controlId) {
1192                    throw 'controlId must be set';
1193                }
1194
1195                if (!options.restUrl) {
1196                    throw 'restUrl must be set';
1197                }
1198
1199                if (options.enhanceForLongLists === true && !options.searchRestUrl) {
1200                    throw 'searchRestUrl must be set';
1201                }
1202
1203                settings = $.extend({}, exports.defaults, options);
1204
1205                if (!settings.defaultText) {
1206                    settings.defaultText = exports.defaults.defaultText;
1207                }
1208
1209                accountPicker = new AccountPicker(settings);
1210                exports.controls[settings.controlId] = accountPicker;
1211                accountPicker.initialize();
1212            }
1213        };
1214
1215        return exports;
1216    }());
1217}(jQuery));
1218;
1219(function ($) {
1220    'use strict';
1221    window.Rock = window.Rock || {};
1222    Rock.controls = Rock.controls || {};
1223
1224    Rock.controls.addressControl = (function () {
1225        var exports = {
1226            initialize: function (options) {
1227                if (!options.id) {
1228                    throw 'id is required';
1229                }
1230            },
1231            clientValidate: function (validator, args) {
1232                var $addressControl = $(validator).closest('.js-addressControl');
1233                var $addressFieldControls = $addressControl.find('.js-address-field.required');
1234                var addressControlLabelText = $addressControl.attr('data-itemlabel');
1235                var isValid = true;
1236                var invalidFieldList = [];
1237
1238                $addressFieldControls.each(function (index, fieldControl) {
1239                    var $fieldControl = $(fieldControl);
1240                    if (typeof fieldControl == 'undefined' || $fieldControl.val().length > 0) {
1241                        $fieldControl.parent('div.form-group').removeClass('has-error');
1242                    }
1243                    else {
1244                        $fieldControl.parent('div.form-group').addClass('has-error');
1245                        invalidFieldList.push($fieldControl.attr('field-name'));
1246                        isValid = false;
1247                    }
1248                });
1249
1250                if (!isValid) {
1251                    validator.errormessage = addressControlLabelText + " field is required: " + invalidFieldList.join(", ");
1252                }
1253
1254                args.IsValid = isValid;
1255            }
1256        };
1257
1258        return exports;
1259    }());
1260}(jQuery));
1261;
1262(function ($) {
1263    'use strict';
1264    window.Rock = window.Rock || {};
1265    Rock.controls = Rock.controls || {};
1266
1267    Rock.controls.assetManager = (function () {
1268        var exports;
1269
1270        exports = {
1271            initialize: function (options) {
1272                var self = this;
1273
1274                var $assetBrowser = $('#' + options.controlId);
1275                var $folderTreeView = $assetBrowser.find('.js-folder-treeview .treeview');
1276                var $selectFolder = $assetBrowser.find('.js-selectfolder');
1277                var $expandedFolders = $assetBrowser.find('.js-expandedFolders');
1278                var $assetStorageId = $assetBrowser.find('.js-assetstorage-id');
1279                var $treePort = $assetBrowser.find('.js-treeviewport');
1280                var $treeTrack = $assetBrowser.find('.js-treetrack');
1281                var $isRoot = $assetBrowser.find('.js-isroot');
1282
1283                var $createFolder = $assetBrowser.find('.js-createfolder');
1284                var $createFolderDiv = $assetBrowser.find('.js-createfolder-div');
1285                var $createFolderInput = $assetBrowser.find('.js-createfolder-input');
1286                var $createFolderCancel = $assetBrowser.find('.js-createfolder-cancel');
1287                var $createFolderAccept = $assetBrowser.find('.js-createfolder-accept');
1288                var $deleteFolder = $assetBrowser.find('.js-deletefolder');
1289                var $createFolderNotification = $assetBrowser.find('.js-createfolder-notification');
1290
1291
1292                // 2020-09-07 MDP
1293                // Asset manager can be used in 3 ways.
1294                // 1) An ItemFromBlockPicker control (An Asset attribute).
1295                // 2) Within the HTML Editor as a summernote plugin.
1296                // 3) In Home > CMS Configuration > Asset Manager.
1297                // 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.
1298                // 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
1299                var $assetManagerModal = $assetBrowser.closest('.js-AssetManager-modal');
1300                var $singleSelectControls;
1301                if ($assetManagerModal.length) {
1302                    // this would be cases #1 and #2
1303                    $singleSelectControls = $assetManagerModal.find('.js-singleselect');
1304                } else {
1305                    // this would be case #3
1306                    $singleSelectControls = $assetBrowser.find('.js-singleselect');
1307                }
1308
1309                var $minSelectControls = $assetBrowser.find('.js-minselect');
1310                var $fileCheckboxes = $assetBrowser.find('.js-checkbox');
1311
1312                var $renameFile = $assetBrowser.find('.js-renamefile');
1313                var $renameFileDiv = $assetBrowser.find('.js-renamefile-div');
1314                var $renameFileInput = $assetBrowser.find('.js-renamefile-input');
1315                var $renameFileCancel = $assetBrowser.find('.js-renamefile-cancel');
1316                var $renameFileAccept = $assetBrowser.find('.js-renamefile-accept');
1317                var $renamefilenotification = $assetBrowser.find('.js-renamefile-notification');
1318
1319                if ($folderTreeView.length === 0) {
1320                    return;
1321                }
1322
1323                // Some buttons need an asset selected in order to work
1324                if ($assetStorageId.val() === "-1") {
1325                    $('.js-assetselect').addClass('aspNetDisabled');
1326                }
1327
1328                $fileCheckboxes.removeAttr('checked');
1329
1330                // Can only add if either an asset or folder is selected
1331                if ($selectFolder.val() === "" && $assetStorageId.val() === "-1") {
1332                    $createFolder.addClass('aspNetDisabled');
1333                }
1334                else {
1335                    $createFolder.removeClass('aspNetDisabled');
1336                }
1337
1338                // Can delete folder only if a folder is selected
1339                if ($selectFolder.val() === "" || $isRoot.val() === "True") {
1340                    $deleteFolder.addClass('aspNetDisabled');
1341                }
1342                else {
1343                    $deleteFolder.removeClass('aspNetDisabled');
1344                }
1345
1346                var folderTreeData = $folderTreeView.data('rockTree');
1347
1348                if (!folderTreeData) {
1349                    var selectedFolders = [encodeURIComponent($assetStorageId.val() + ',' + $selectFolder.val())];
1350                    var expandedFolders = $expandedFolders.val().split('||');
1351                    expandedFolders.forEach(function (part, i, array) {
1352                        array[i] = encodeURIComponent(array[i]);
1353                    });
1354
1355                    $folderTreeView.rockTree({
1356                        restUrl: options.restUrl,
1357                        selectedIds: selectedFolders,
1358                        expandedIds: expandedFolders
1359                    });
1360
1361                    var treePortIScroll = new IScroll($treePort[0], {
1362                        mouseWheel: false,
1363                        scrollX: true,
1364                        scrollY: false,
1365                        indicators: {
1366                            el: '#' + $treeTrack.attr('id'),
1367                            interactive: true,
1368                            resize: false,
1369                            listenY: false,
1370                            listenX: true
1371                        },
1372                        click: false,
1373                        preventDefaultException: { tagName: /.*/ }
1374                    });
1375
1376                    // resize scrollbar when the window resizes
1377                    $(document).ready(function () {
1378                        $(window).on('resize', function () {
1379                            resizeScrollAreaHeight();
1380                        });
1381                    });
1382
1383                    $folderTreeView.on('rockTree:dataBound rockTree:rendered', function (evt) {
1384                        resizeScrollAreaHeight();
1385                    });
1386
1387                    $folderTreeView.on('rockTree:expand rockTree:collapse', function (evt, data) {
1388                        resizeScrollAreaHeight();
1389
1390                        // get the data-id values of rock-tree items that are showing visible children (in other words, Expanded Nodes)
1391                        var expandedDataIds = $(evt.currentTarget).find('.rocktree-children').filter(":visible").closest('.rocktree-item').map(function () {
1392                            var dataId = $(this).attr('data-id');
1393                            if (dataId !== data) {
1394                                return decodeURIComponent(dataId);
1395                            }
1396                        }).get().join('||');
1397
1398                        $expandedFolders.val(expandedDataIds);
1399                    });
1400                }
1401
1402                function resizeScrollAreaHeight() {
1403                    var overviewHeight = $folderTreeView.closest('.overview').height();
1404                    $treePort.height(overviewHeight);
1405
1406                    if (treePortIScroll) {
1407                        treePortIScroll.refresh();
1408                    }
1409                }
1410
1411                function isValidName(name) {
1412                    var regex = new RegExp("^[^*/><?\\\\|:,~]+$");
1413                    return regex.test(name);
1414                }
1415
1416                $folderTreeView.off('rockTree:selected').on('rockTree:selected', function (e, data) {
1417                    var assetFolderIdParts = unescape(data).split(",");
1418                    var storageId = assetFolderIdParts[0] || "";
1419                    var folder = assetFolderIdParts[1] || "";
1420                    var isRoot = assetFolderIdParts[2] || false;
1421                    var postbackArg;
1422                    var expandedFolders = encodeURIComponent($expandedFolders.val());
1423
1424                    // Can only add if either an asset or folder is selected
1425                    if ($selectFolder.val() === "" && $assetStorageId.val() === "-1") {
1426                        $createFolder.addClass('aspNetDisabled');
1427                    }
1428                    else {
1429                        $createFolder.removeClass('aspNetDisabled');
1430                    }
1431
1432                    // Can delete folder only if a folder is selected
1433                    if ($selectFolder.val() === "" || $isRoot.val() === "True") {
1434                        $deleteFolder.addClass('aspNetDisabled');
1435                    }
1436                    else {
1437                        $deleteFolder.removeClass('aspNetDisabled');
1438                    }
1439
1440                    if ($selectFolder.val() !== folder || $assetStorageId.val() !== storageId) {
1441                        $selectFolder.val(folder);
1442                        $assetStorageId.val(storageId);
1443                        $isRoot.val(isRoot);
1444                        postbackArg = 'storage-id:' + storageId + '?folder-selected:' + folder.replace(/\\/g, "/").replace("'", "\\'") + '?expanded-folders:' + expandedFolders.replace("'", "\\'") + '?isRoot:' + isRoot;
1445
1446                        var jsPostback = "javascript:__doPostBack('" + options.filesUpdatePanelId + "','" + postbackArg + "');";
1447                        window.location = jsPostback;
1448                    }
1449                });
1450
1451                $fileCheckboxes.off('click').on('click', function () {
1452                    var numberChecked = $fileCheckboxes.filter(':checked').length;
1453
1454                    if (numberChecked === 0) {
1455                        $singleSelectControls.addClass('aspNetDisabled');
1456                        $('.js-minselect').addClass('aspNetDisabled');
1457                    }
1458                    else if (numberChecked === 1) {
1459                        $singleSelectControls.removeClass('aspNetDisabled');
1460                        $minSelectControls.removeClass('aspNetDisabled');
1461                    }
1462                    else {
1463                        $singleSelectControls.addClass('aspNetDisabled');
1464                        $minSelectControls.removeClass('aspNetDisabled');
1465                    }
1466                });
1467
1468                $createFolder.off('click').on('click', function () {
1469                    $createFolderDiv.fadeToggle();
1470                    $createFolderInput.val('');
1471                });
1472
1473                $createFolderCancel.off('click').on('click', function () {
1474                    $createFolderDiv.fadeOut();
1475                    $createFolderInput.val('');
1476                    $createFolderNotification.hide().text('');
1477                });
1478
1479                $createFolderAccept.off('click').on('click', function (e) {
1480                    var name = $createFolderInput.val();
1481
1482                    if (name === "") {
1483                        $createFolderNotification.show().text('Folder name is required.');
1484                        e.preventDefault();
1485                    }
1486
1487                    if (!isValidName(name)) {
1488                        $createFolderNotification.show().text('Invalid characters in path');
1489                        e.preventDefault();
1490                    }
1491                });
1492
1493                $renameFile.off('click').on('click', function () {
1494                    $renameFileDiv.fadeToggle();
1495                    $renameFileInput.val('');
1496                });
1497
1498                $renameFileCancel.off('click').on('click', function () {
1499                    $renameFileDiv.fadeOut();
1500                    $renameFileInput.val('');
1501                    $renamefilenotification.hide().text('');
1502                });
1503
1504                $renameFileAccept.off('click').on('click', function (e) {
1505                    var name = $renameFileInput.val();
1506
1507                    if (name === "") {
1508                        $renamefilenotification.show().text('File name is required.');
1509                        e.preventDefault();
1510                    }
1511
1512                    if (!isValidName(name)) {
1513                        $renamefilenotification.show().text('Invalid characters in file name.');
1514                        e.preventDefault();
1515                    }
1516                });
1517
1518                $deleteFolder.off('click').on('click', function (e) {
1519                    if ($(this).attr('disabled') === 'disabled') {
1520                        return false;
1521                    }
1522
1523                    Rock.dialogs.confirmDelete(e, 'folder and all its contents');
1524                });
1525            }
1526        };
1527
1528        return exports;
1529
1530    }());
1531}(jQuery));
1532;
1533(function ($) {
1534    'use strict';
1535    window.Rock = window.Rock || {};
1536    Rock.controls = Rock.controls || {};
1537
1538    Rock.controls.autoCompleteDropDown = (function () {
1539        var exports,
1540            AutoCompleteDropDown = function (options) {
1541                this.controlId = options.controlId;
1542                this.valueControlId = options.valueControlId;
1543                this.$el = $('#' + this.controlId);
1544                this.name = options.name;
1545                this.url = options.url;
1546                this.limit = options.limit;
1547                this.idProperty = options.idProperty;
1548                this.valuekey = options.valuekey;
1549                this.template = options.template;
1550                this.header = options.header;
1551                this.footer = options.footer;
1552            };
1553
1554        AutoCompleteDropDown.prototype = {
1555            constructor: AutoCompleteDropDown,
1556            initialize: function () {
1557                var self = this,
1558                    Liquid = require('liquid'),
1559                    liquidEngine = {
1560                        compile: function (template) {
1561                            var parsedTemplate = Liquid.Template.parse(template);
1562                            return {
1563                                render: function (context) {
1564                                    return parsedTemplate.render(context);
1565                                }
1566                            };
1567                        }
1568                    };
1569
1570                this.$el.typeahead({
1571                    name: this.name,
1572                    limit: this.limit,
1573                    valueKey: this.valuekey,
1574                    template: this.template,
1575                    header: this.header,
1576                    footer: this.footer,
1577                    engine: liquidEngine,
1578                    remote: {
1579                        url: Rock.settings.get('baseUrl') + this.url,
1580                        filter: function (response) {
1581                            response.forEach(function (item) {
1582                                item['tokens'] = item[self.valuekey].replace(/,/g, '').split(" ");
1583                            });
1584                            return response;
1585                        }
1586                    }
1587                });
1588
1589                this.initializeEventHandlers();
1590            },
1591
1592            initializeEventHandlers: function () {
1593                var self = this,
1594                    idProperty = this.idProperty,
1595                    setValue = function (datum) {
1596                        $('#' + self.valueControlId).val(datum[idProperty]);
1597                    };
1598
1599                // Listen for typeahead's custom events and trigger search when hit
1600                this.$el.on('typeahead:selected typeahead:autocompleted', function (e, obj, name) {
1601                    setValue(obj);
1602                    if (typeof self.onSelected === "function") {
1603                        self.onSelected(obj);
1604                    }
1605                });
1606
1607            }
1608        };
1609
1610        exports = {
1611            defaults: {
1612                controlId: null,
1613                name: 'autoComplete',
1614                valueControlId: null,
1615                limit: 5,
1616                idProperty: 'Id',
1617                valuekey: 'value',
1618                template: '<p>{{value}}</p>',
1619                header: '',
1620                footer: ''
1621            },
1622            initialize: function (options) {
1623                var autoCompleteDropDown,
1624                    settings = $.extend({}, exports.defaults, options);
1625
1626                if (!settings.controlId) throw 'controlId is required';
1627                if (!settings.valueControlId) throw 'valueControlId is required';
1628                if (!settings.url) throw 'url is required';
1629
1630                autoCompleteDropDown = new AutoCompleteDropDown(settings);
1631
1632                // Delay initialization until after the DOM is ready
1633                $(function () {
1634                    autoCompleteDropDown.initialize();
1635                });
1636
1637                return autoCompleteDropDown;
1638            }
1639        };
1640
1641        return exports;
1642    }());
1643}(jQuery));
1644;
1645/*!
1646 * Bootstrap Colorpicker v2.5.3
1647 * https://itsjavi.com/bootstrap-colorpicker/
1648 */
1649!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:"90ee9
16490",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||
vendor: 6,075 bytes, line 1649
1649!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("c
1649olorpicker","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
1649),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});
1650;
1651(function ($) {
1652
1653  $.fn.rating = function () {
1654
1655    var element;
1656
1657    // A private function to highlight a star corresponding to a given value
1658    function _paintValue(ratingInput, value, active_icon, inactive_icon) {
1659      var selectedStar = $(ratingInput).find('[data-value="' + value + '"]');
1660      selectedStar.removeClass(inactive_icon).addClass(active_icon);
1661      selectedStar.prevAll('[data-value]').removeClass(inactive_icon).addClass(active_icon);
1662      selectedStar.nextAll('[data-value]').removeClass(active_icon).addClass(inactive_icon);
1663    }
1664
1665    // A private function to remove the highlight for a selected rating
1666    function _clearValue(ratingInput, active_icon, inactive_icon) {
1667      var self = $(ratingInput);
1668      self.find('[data-value]').removeClass(active_icon).addClass(inactive_icon);
1669    }
1670
1671    // A private function to change the actual value to the hidden field
1672    function _updateValue(input, val) {
1673      input.val(val).trigger('change');
1674      if (val === input.data('empty-value')) {
1675        input.siblings('.rating-clear').hide();
1676      } else {
1677        input.siblings('.rating-clear').show();
1678      }
1679    }
1680
1681
1682    // Iterate and transform all selected inputs
1683    for (element = this.length - 1; element >= 0; element--) {
1684
1685      var el, i,
1686        originalInput = $(this[element]),
1687        max = originalInput.data('max') || 5,
1688        min = originalInput.data('min') || 0,
1689        def_val = originalInput.val() || 0,
1690        lib = originalInput.data('icon-lib') || 'glyphicon',
1691        active = originalInput.data('active-icon') || 'glyphicon-star',
1692        inactive = originalInput.data('inactive-icon') || 'glyphicon-star-empty',
1693        clearable = originalInput.data('clearable') || null,
1694        clearable_i = originalInput.data('clearable-icon') || 'glyphicon-remove',
1695        stars = '';
1696
1697      // HTML element construction
1698      for (i = min; i <= max; i++) {
1699        // Create <max> empty stars
1700        if(i  <= def_val){
1701          stars += ['<i class="',lib, ' ', active, '" data-value="', i, '"></i>'].join('');
1702          }
1703        else{
1704            stars += ['<i class="',lib, ' ', inactive, '" data-value="', i, '"></i>'].join('')
1705            }
1706      }
1707      // Add a clear link if clearable option is set
1708      if (clearable) {
1709          stars += [
1710          ' <a class="rating-clear" style="display:none;" href="javascript:void">',
1711          '<span class="',lib,' ',clearable_i,'"></span> ',
1712          clearable,
1713          '</a>'].join('');
1714      }
1715
1716      // Clone with data and events the original input to preserve any additional data and event bindings.
1717      var newInput = originalInput.clone(true)
1718        .addClass('hidden')
1719        .data('max', max)
1720        .data('min', min)
1721        .data('icon-lib', lib)
1722        .data('active-icon', active)
1723        .data('inactive-icon', inactive);
1724
1725      // Rating widget is wrapped inside a div
1726      el = [
1727        '<div class="rating-input">',
1728        stars,
1729        '</div>'].join('');
1730
1731      // Replace original inputs HTML with the new one
1732      if (originalInput.parents('.rating-input').length <= 0) {
1733        originalInput.replaceWith($(el).append(newInput));
1734      }
1735
1736    }
1737
1738    // Give live to the newly generated widgets
1739    $('.rating-input')
1740      // Highlight stars on hovering
1741      .on('mouseenter', '[data-value]', function () {
1742        var self = $(this);
1743         input = self.siblings('input');
1744        _paintValue(self.closest('.rating-input'), self.data('value'), input.data('active-icon'), input.data('inactive-icon'));
1745      })
1746      // View current value while mouse is out
1747      .on('mouseleave', '[data-value]', function () {
1748        var self = $(this),
1749          input = self.siblings('input'),
1750          val = input.val(),
1751          min = input.data('min'),
1752          max = input.data('max'),
1753          active = input.data('active-icon'),
1754          inactive = input.data('inactive-icon');
1755        if (val >= min && val <= max) {
1756          _paintValue(self.closest('.rating-input'), val, active, inactive);
1757        } else {
1758          _clearValue(self.closest('.rating-input'), active, inactive);
1759        }
1760      })
1761      // Set the selected value to the hidden field
1762      .on('click', '[data-value]', function (e) {
1763        var self = $(this),
1764          val = self.data('value'),
1765          input = self.siblings('input');
1766        _updateValue(input,val);
1767        e.preventDefault();
1768        return false;
1769      })
1770      // Remove value on clear
1771      .on('click', '.rating-clear', function (e) {
1772        var self = $(this),
1773          input = self.siblings('input'),
1774          active = input.data('active-icon'),
1775          inactive = input.data('inactive-icon');
1776        _updateValue(input, input.data('empty-value'));
1777        _clearValue(self.closest('.rating-input'), active, inactive);
1778        e.preventDefault();
1779        return false;
1780      })
1781      // Initialize view with default value
1782      .each(function () {
1783        var input = $(this).find('input'),
1784          val = input.val(),
1785          min = input.data('min'),
1786          max = input.data('max');
1787        if (val !== "" && +val >= min && +val <= max) {
1788          _paintValue(this, val);
1789          $(this).find('.rating-clear').show();
1790        }
1791        else {
1792          input.val(input.data('empty-value'));
1793          _clearValue(this);
1794        }
1795      });
1796
1797  };
1798
1799  // Auto apply conversion of number fields with class 'rating' into rating-fields
1800  $(function () {
1801    if ($('input.rating[type=number]').length > 0) {
1802      $('input.rating[type=number]').rating();
1803    }
1804  });
1805
1806}(jQuery));
1807;
1808(function ($) {
1809    'use strict';
1810    window.Rock = window.Rock || {};
1811    Rock.controls = Rock.controls || {};
1812
1813    Rock.controls.bootstrapButton = (function () {
1814
1815        var exports = {
1816
1817            showLoading: function (btn) {
1818
1819                if (typeof (Page_ClientValidate) === 'function') {
1820
1821                    if (typeof Page_IsValid === 'undefined' || Page_IsValid) {
1822                        // make sure page really is valid
1823                        Page_ClientValidate();
1824                    }
1825                }
1826
1827                var $btn = $(btn);
1828
1829                if (typeof Page_IsValid === 'undefined' || Page_IsValid) {
1830                    setTimeout(function () {
1831                        $btn.prop('disabled', true);
1832                        $btn.attr('disabled', 'disabled');
1833                        $btn.addClass('disabled');
1834                        $btn.html($btn.attr('data-loading-text'));
1835                    }, 0)
1836                }
1837
1838                return true;
1839            },
1840
1841            onCompleted: function (btn) {
1842                var $btn = $(btn);
1843                $btn.prop('disabled', true);
1844                $btn.attr('disabled', 'disabled');
1845                $btn.addClass('disabled');
1846                var completedTxt = $btn.attr('data-completed-text');
1847                if (completedTxt && completedTxt !== '') {
1848                    $btn.html(completedTxt);
1849                } else {
1850                    $btn.html($btn.attr('data-init-text'));
1851                }
1852
1853                var completedMessage = $btn.attr('data-completed-message');
1854                if (completedMessage && completedMessage !== '') {
1855                    var id = $btn.attr("id") + "_msg";
1856                    var $span = $('<span />').attr('id', id).html(completedMessage);
1857                    $btn.after( $span );
1858                }
1859
1860                var timeout = Number($btn.attr('data-timeout-text'));
1861                if ( timeout && !isNaN(timeout)) {
1862                    timeout = timeout * 1000;
1863                    setTimeout(function () {
1864                        $btn.prop('disabled', false);
1865                        $btn.removeAttr('disabled');
1866                        $btn.removeClass('disabled');
1867                        $btn.html($btn.attr('data-init-text'));
1868                        var id = $btn.attr("id") + "_msg";
1869                        var $span = $('#' + id);
1870                        if ($span){
1871                            $span.remove();
1872                        }
1873                    }, timeout);
1874                }
1875            }
1876        };
1877
1878        return exports;
1879    }());
1880}(jQuery));
1881;
1882(function ($) {
1883    'use strict';
1884    window.Rock = window.Rock || {};
1885    Rock.controls = Rock.controls || {};
1886
1887    Rock.controls.buttonDropDownList = (function () {
1888        var exports = {
1889            initialize: function (options) {
1890                if (!options.controlId) {
1891                    throw 'id is required';
1892                }
1893
1894                var $control = $('#' + options.controlId);
1895                var $selectBtn = $control.find('.js-buttondropdown-btn-select');
1896                var $selectedId = $control.find('.js-buttondropdown-selected-id');
1897                var checkmarksEnabled = $control.attr('data-checkmarks-enabled') == 1;
1898
1899                $('.dropdown-menu a', $control).on('click', function (e) {
1900                    var $el = $(this);
1901                    var text = $el.html();
1902                    var textHtml = $el.html() + " <span class='ti ti-caret-down-filled' ></span >";
1903                    var idValue = $el.attr('data-id');
1904                    var postbackScript = $el.attr('data-postback-script');
1905
1906                    if (checkmarksEnabled) {
1907                        $el.closest('.dropdown-menu').find('.js-selectionicon').removeClass('ti-check');
1908                        $el.find('.js-selectionicon').addClass('ti-check');
1909                    }
1910                    else {
1911                        $selectBtn.html(textHtml);
1912                    }
1913
1914                    $selectedId.val(idValue);
1915
1916                    if (postbackScript) {
1917                        window.location = postbackScript;
1918                    }
1919                });
1920
1921            }
1922        };
1923
1924        return exports;
1925    }());
1926}(jQuery));
1927
1928
1929
1930;
1931(function ($) {
1932    'use strict';
1933    window.Rock = window.Rock || {};
1934    Rock.controls = Rock.controls || {};
1935
1936    Rock.controls.buttonGroup = (function () {
1937        var exports = {
1938            initialize: function (options) {
1939                if (!options.id) {
1940                    throw 'id is required';
1941                }
1942
1943                var $buttonGroup = $('#' + options.id);
1944                var $buttonGroupItems = $buttonGroup.find('.js-buttongroup-item');
1945                var selectedItemClass = $buttonGroup.attr('data-selecteditemclass');
1946                var unselectedItemClass = $buttonGroup.attr('data-unselecteditemclass');
1947
1948                $buttonGroupItems.on('click', function () {
1949                    var $selectedItem = $(this);
1950                    var $unselectedItems = $buttonGroupItems.not($selectedItem);
1951                    $unselectedItems.removeClass(selectedItemClass).addClass(unselectedItemClass);
1952                    $selectedItem.removeClass(unselectedItemClass).addClass(selectedItemClass);
1953
1954                    // make sure the input elements onclick get executed so that the postback gets fired (if there is one)
1955                    var $input = $selectedItem.find('input');
1956                    var clickFunction = $input.prop('onclick');
1957                    if (clickFunction) {
1958                        clickFunction();
1959                    }
1960                });
1961
1962            }
1963        };
1964
1965        return exports;
1966    }());
1967}(jQuery));
1968;
1969(function ($) {
1970    'use strict';
1971    window.Rock = window.Rock || {};
1972    Rock.controls = Rock.controls || {};
1973
1974    // Cloudflare Turnstile docs:
1975    // https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/
1976
1977    Rock.controls.captcha = (function () {
1978        // Keep track of the last-rendered Turnstile widget ID for each Rock Captcha
1979        // control, so we can manage multiple Turnstile widgets per page.
1980        var turnstileIds = {};
1981
1982        /**
1983         * Gets whether the Turnstile module is ready.
1984         * 
1985         * @returns Whether the Turnstile module is ready.
1986         */
1987        function isTurnstileReady() {
1988            return typeof turnstile !== 'undefined';
1989        }
1990
1991        /**
1992         * Tries to render a Turnstile widget for a given Rock Captcha control instance.
1993         * 
1994         * @param {any} config The configuration info for this Rock Captcha control instance.
1995         */
1996        function tryRenderTurnstile(config) {
1997            if (!isTurnstileReady()) {
1998                return;
1999            }
2000
2001            // Remove any previous widget for this Rock Captcha control to prevent
2002            // Turnstile from logging warnings/errors to the console.
2003            tryRemoveTurnstile(config.id);
2004
2005            // Keep track of the last-rendered Turnstile widget's ID, as we'll
2006            // need to intstruct the Turnstile library not to track it anymore
2007            // (and we'll create a new one) for each request. Note that once the
2008            // challenge has been solved, it's up to the Rock block or parent
2009            // control to dictate that a Turnstile should no longer be rendered,
2010            // so our job here is to simply keep rendering a new widget on every
2011            // request, regardless of whether this is a full or partial postback.
2012            var turnstileId = turnstile.render('#' + config.id, {
2013                siteKey: config.key,
2014                callback: function (token) {
2015                    var $hfToken = $('#' + config.id + '_hfToken');
2016                    $hfToken.val(token);
2017
2018                    if (token) {
2019                        // Allow time for the success animation to be displayed.
2020                        setTimeout(function () {
2021                            if (config.postBackScript) {
2022                                window.location = "javascript:" + config.postBackScript;
2023                            }
2024                        }, 750);
2025                    }
2026                },
2027                'error-callback': function (errorCode) {
2028                    if (errorCode === '300030') {
2029                        // This can happen if a Turnstile widget was loaded into
2030                        // the DOM and is no longer available (i.e. after a
2031                        // partial postback results in the Rock Captcha
2032                        // control no longer being visible).
2033                        tryRemoveTurnstile(config.id);
2034
2035                        // Return true to prevent Turnstile from repeatedly logging to the console.
2036                        // https://developers.cloudflare.com/turnstile/troubleshooting/client-side-errors/#error-handling
2037                        return true;
2038
2039                        // However, there is an issue where an already-queued
2040                        // `.reset()` invocation will fail and still log an
2041                        // error to the console; hopefully this scenario is
2042                        // not common.
2043                        // https://community.cloudflare.com/t/window-turnstile-remove-with-retry-set-to-auto-doesnt-cleartimeout/525530
2044                    }
2045                }
2046            });
2047
2048            turnstileIds[config.id] = turnstileId;
2049        }
2050
2051        /**
2052         * Tries to remove a Turnstile widget for the provided Rock Captcha control ID.
2053         * 
2054         * @param {any} rockCaptchaId The Rock Captcha control ID.
2055         */
2056        function tryRemoveTurnstile(rockCaptchaId) {
2057            // Try to find this Rock Captcha control's last-rendered Turnstile widget.
2058            var turnstileId = turnstileIds[rockCaptchaId];
2059            if (turnstileId) {
2060                // Inform the Turnstile library that the old Turnstile widget
2061                // should no longer be tracked.
2062                turnstile.remove(turnstileId);
2063                delete turnstileIds[rockCaptchaId];
2064            }
2065        }
2066
2067        var exports = {
2068            initialize: function (config) {
2069                if (!config) {
2070                    return;
2071                }
2072
2073                if (!config.id) {
2074                    throw 'id is required';
2075                }
2076
2077                if (!config.key) {
2078                    throw 'key is required';
2079                }
2080
2081                // Ensure Cloudflare's Turnstile script is added to the page.
2082                if (!$('#rockCloudflareTurnstile').length && !isTurnstileReady()) {
2083                    // By default, jQuery adds a cache-busting parameter on dynamically-added scripts.
2084                    // Set `cache: true` to avoid this, and ensure we only load the script once.
2085                    $.ajaxSetup({ cache: true });
2086                    $.getScript('https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit')
2087                        .done(function () {
2088                            tryRenderTurnstile(config);
2089                        })
2090                        .fail(function () {
2091                            console.error("Unable to load Captcha library.");
2092                        });
2093
2094                    return;
2095                }
2096
2097                // The Turnstile script was already added by the C# control;
2098                // just wait for the DOM to finish loading.
2099                $(document).ready(function () {
2100                    tryRenderTurnstile(config);
2101                });
2102            },
2103            clientValidate: function (validator, args) {
2104                var $validator = $(validator);
2105                var isRequired = $validator.data('required');
2106
2107                var rockCaptchaId = $validator.data('captcha-id');
2108                var $formGroup = $('#' + rockCaptchaId).closest('.form-group');
2109
2110                if (isRequired && isTurnstileReady()) {
2111                    // Get this validator control's related Rock Captcha control ID and
2112                    // try to find the underlying Turnstile widget.
2113                    var turnstileId = turnstileIds[rockCaptchaId];
2114                    if (turnstileId) {
2115                        var turnstileResponse = turnstile.getResponse(turnstileId);
2116                    }
2117                }
2118
2119                var isValid = !!(!isRequired || turnstileResponse);
2120
2121                if (isValid) {
2122                    args.IsValid = true;
2123                    $formGroup.removeClass('has-error');
2124                }
2125                else {
2126                    args.IsValid = false;
2127                    $formGroup.addClass('has-error');
2128                }
2129            }
2130        };
2131
2132        return exports;
2133    }());
2134}(jQuery));
2135;
2136(function ($) {
2137    'use strict';
2138    window.Rock = window.Rock || {};
2139
2140    Rock.controls.charts = (function () {
2141        var exports = {
2142
2143            ///
2144            /// handles putting chartData into a Line/Bar/Points chart
2145            ///
2146            plotChartData: function (chartData, chartOptions, plotSelector, yaxisLabelText, getSeriesPartitionNameUrl, combineValues) {
2147
2148                var chartSeriesLookup = {};
2149                var chartSeriesList = [];
2150
2151                var chartGoalPoints = {
2152                    label: yaxisLabelText + ' Goal',
2153                    chartData: [],
2154                    data: []
2155                }
2156
2157                if (chartOptions.customSettings) {
2158                    chartGoalPoints.color = chartOptions.customSettings.goalSeriesColor;
2159                }
2160
2161                // populate the chartMeasurePoints data array with data from the REST result
2162                for (var i = 0; i < chartData.length; i++) {
2163                    if (chartData[i].MetricValueType && chartData[i].MetricValueType == 1) {
2164                        // if the chartdata is marked as a Metric Goal Value Type, populate the chartGoalPoints
2165                        chartGoalPoints.data.push([chartData[i].DateTimeStamp, chartData[i].YValue]);
2166                        chartGoalPoints.chartData.push(chartData[i]);
2167                    }
2168                    else {
2169                        var lookupKey = chartData[i].MetricValuePartitionEntityIds;
2170                        if (!lookupKey || lookupKey == '') {
2171                            lookupKey = chartData[i].SeriesName;
2172                        }
2173
2174                        if (!chartSeriesLookup[lookupKey]) {
2175
2176                            // If SeriesName is specified, that can be the name of the series if MetricValuePartitionEntityIds is blank
2177                            var seriesName = chartData[i].SeriesName;
2178
2179                            // if we aren't combining values, we'll have to lookup the series name based on MetricValuePartitionEntityIds
2180                            if (chartData[i].MetricValuePartitionEntityIds && chartData[i].MetricValuePartitionEntityIds != '' && !combineValues)
2181                            {
2182                                // MetricValuePartitionEntityIds is not blank so get the seriesName from the getSeriesPartitionNameUrl
2183                                if (getSeriesPartitionNameUrl) {
2184                                    var metricValuePartitionEntityIdDataJSON = JSON.stringify(chartData[i].MetricValuePartitionEntityIds.split(','));
2185
2186                                    $.ajax({
2187                                        type: 'POST',
2188                                        url: getSeriesPartitionNameUrl,
2189                                        data: metricValuePartitionEntityIdDataJSON,
2190                                        async: false,
2191                                        success: null,
2192                                        contentType: 'application/json'
2193                                    })
2194                                    .done(function (data) {
2195                                        seriesName = data;
2196                                    });
2197                                }
2198                            }
2199
2200                            // either either seriesName, yaxisLabelText or just 'value' if the seriesname isn't defined
2201                            seriesName = seriesName || yaxisLabelText || 'value';
2202
2203                            chartSeriesLookup[lookupKey] = {
2204                                label: seriesName,
2205                                chartData: [],
2206                                data: []
2207                            };
2208                        }
2209
2210                        chartSeriesLookup[lookupKey].data.push([chartData[i].DateTimeStamp, chartData[i].YValue]);
2211                        chartSeriesLookup[lookupKey].chartData.push(chartData[i]);
2212                    }
2213                }
2214
2215                // setup the series list (goal points last, if they exist)
2216                for (var chartSeriesKey in chartSeriesLookup) {
2217                    var chartMeasurePoints = chartSeriesLookup[chartSeriesKey];
2218                    if (chartMeasurePoints.data.length) {
2219                        chartSeriesList.push(chartMeasurePoints);
2220                    }
2221                }
2222
2223                if (combineValues && chartSeriesList.length != 1) {
2224
2225                    var combinedDataLookup = {};
2226                    var chartMeasurePoints = null;
2227
2228                    for (var chartMeasurePointsId in chartSeriesList) {
2229
2230                        chartMeasurePoints = chartSeriesList[chartMeasurePointsId];
2231
2232                        $.each(chartMeasurePoints.data, function (indexInArray, dataPair) {
2233                            var dateTimeIndex = dataPair[0];
2234                            var combinedItem = combinedDataLookup[dateTimeIndex];
2235                            if (combinedItem) {
2236                                // sum the YValue of the .data
2237                                combinedItem.chartData.YValue += chartMeasurePoints.chartData[indexInArray].YValue;
2238                                combinedItem.data[1] += dataPair[1];
2239                            }
2240                            else {
2241                                combinedDataLookup[dateTimeIndex] = {
2242                                    chartData: chartMeasurePoints.chartData[indexInArray],
2243                                    data: dataPair
2244                                };
2245                            }
2246                        });
2247                    }
2248
2249                    var combinedData = [];
2250                    var combinedChartData = [];
2251
2252                    for (var dataLookupId in combinedDataLookup) {
2253                        combinedData.push(combinedDataLookup[dataLookupId].data);
2254                        combinedChartData.push(combinedDataLookup[dataLookupId].chartData);
2255                    }
2256
2257                    chartSeriesList = [];
2258
2259                    // sort data after combining
2260                    combinedChartData.sort(function (item1, item2) { return item2.DateTimeStamp - item1.DateTimeStamp });
2261                    combinedData.sort(function (item1, item2) { return item2[0] - item1[0]; });
2262
2263                    var chartCombinedMeasurePoints = {
2264                        label: yaxisLabelText + ' Total',
2265                        chartData: combinedChartData,
2266                        data: combinedData
2267                    };
2268
2269
2270                    chartSeriesList.push(chartCombinedMeasurePoints);
2271                }
2272
2273                if (chartGoalPoints.data.length) {
2274                    chartSeriesList.push(chartGoalPoints);
2275                }
2276
2277                // plot the chart
2278                if (chartSeriesList.length > 0) {
2279                    $.plot(plotSelector, chartSeriesList, chartOptions);
2280                }
2281                else {
2282                    $(plotSelector).html('<div class="alert alert-info">No Data Found</div>');
2283                }
2284            },
2285
2286            ///
2287            /// handles putting chart data into a piechart
2288            ///
2289            plotPieChartData: function (chartData, chartOptions, plotSelector, getSeriesPartitionNameUrl) {
2290                var pieData = [];
2291
2292                // populate the chartMeasurePoints data array with data from the REST result for pie data
2293                for (var i = 0; i < chartData.length; i++) {
2294
2295                    pieData.push({
2296                        label: chartData[i].MetricTitle,
2297                        data: chartData[i].YValueTotal,
2298                        chartData: [chartData[i]]
2299                    });
2300                }
2301
2302                if (pieData.length > 0) {
2303                    // plot the pie chart
2304                    $.plot(plotSelector, pieData, chartOptions);
2305                }
2306                else {
2307                    $(plotSelector).html('<div class="alert alert-info">No Data Found</div>');
2308                }
2309            },
2310
2311            ///
2312            /// handles putting chart data into a bar chart
2313            ///
2314            plotBarChartData: function (chartData, chartOptions, plotSelector, getSeriesPartitionNameUrl) {
2315                var barData = [];
2316                var combinedData = {};
2317                var seriesLabels = [];
2318                var seriesNameLookup = {};
2319
2320                // populate the chartMeasurePoints data array with data from the REST result for bar chart data
2321                for (var i = 0; i < chartData.length; i++) {
2322
2323                    var seriesCategory = chartData[i].SeriesName;
2324                    if (chartData[i].MetricValuePartitionEntityIds)
2325                    {
2326                        if (!seriesNameLookup[chartData[i].MetricValuePartitionEntityIds])
2327                        {
2328                            // MetricValuePartitionEntityIds is not blank so get the seriesName from the getSeriesPartitionNameUrl
2329                            if (getSeriesPartitionNameUrl) {
2330                                var metricValuePartitionEntityIdDataJSON = JSON.stringify(chartData[i].MetricValuePartitionEntityIds.split(','));
2331
2332                                $.ajax({
2333                                    type: 'POST',
2334                                    url: getSeriesPartitionNameUrl,
2335                                    data: metricValuePartitionEntityIdDataJSON,
2336                                    async: false,
2337                                    success: null,
2338                                    contentType: 'application/json'
2339                                })
2340                                .done(function (data) {
2341                                    seriesNameLookup[chartData[i].MetricValuePartitionEntityIds] = data;
2342                                });
2343                            }
2344                        }
2345
2346                        seriesCategory = seriesNameLookup[chartData[i].MetricValuePartitionEntityIds] || seriesCategory;
2347                    }
2348
2349                    if (!combinedData[seriesCategory])
2350                    {
2351                        combinedData[seriesCategory] = 0;
2352                    }
2353
2354                    combinedData[seriesCategory] += chartData[i].YValue;
2355                }
2356
2357                var seriesId = 0;
2358                for (var combinedDataKey in combinedData) {
2359
2360                    barData.push([combinedDataKey, combinedData[combinedDataKey]]);
2361                    seriesId++;
2362                    seriesLabels.push(combinedDataKey);
2363                }
2364
2365                seriesLabels.push(seriesCategory);
2366
2367                if (barData.length > 0) {
2368                    // plot the bar chart
2369                    chartOptions.series.chartData = chartData;
2370                    chartOptions.series.labels = seriesLabels;
2371                    $.plot(plotSelector, [barData], chartOptions);
2372                }
2373                else {
2374                    $(plotSelector).html('<div class="alert alert-info">No Data Found</div>');
2375                }
2376            },
2377
2378            ///
2379            /// attaches a bootstrap style tooltip to the 'plothover' of a chart
2380            ///
2381            bindTooltip: function (plotContainerId, tooltipFormatter) {
2382                // setup of bootstrap tooltip which we'll show on the plothover event
2383                var toolTipId = 'tooltip_' + plotContainerId;
2384                var chartContainer = '#' + plotContainerId;
2385
2386                $("<div id=" + toolTipId + " class='tooltip top'><div class='tooltip-inner'></div><div class='tooltip-arrow'></div></div>").css({
2387                    position: 'absolute',
2388                    display: 'none',
2389                }).appendTo('body');
2390
2391                var $toolTip = $('#' + toolTipId);
2392
2393                $(chartContainer).bind('plothover', function (event, pos, item) {
2394
2395                    if (item) {
2396
2397                        var tooltipText = "";
2398
2399                        // if a tooltipFormatter is specified, use that
2400                        if (tooltipFormatter) {
2401                            tooltipText = tooltipFormatter(item);
2402                        }
2403                        else {
2404                            if (item.series.chartData) {
2405                                if (item.series.chartData[item.dataIndex].DateTimeStamp) {
2406                                    tooltipText = new Date(item.series.chartData[item.dataIndex].DateTimeStamp).toLocaleDateString();
2407                                };
2408
2409                                if (item.series.chartData[item.dataIndex].StartDateTimeStamp) {
2410                                    tooltipText = new Date(item.series.chartData[item.dataIndex].StartDateTimeStamp).toLocaleDateString();
2411                                }
2412
2413                                if (item.series.chartData[item.dataIndex].EndDateTimeStamp) {
2414                                    tooltipText += " to " + new Date(item.series.chartData[item.dataIndex].EndDateTimeStamp).toLocaleDateString();
2415                                }
2416
2417                                if (item.series.chartData[item.dataIndex].MetricTitle) {
2418                                    tooltipText = item.series.chartData[item.dataIndex].MetricTitle;
2419                                }
2420                            }
2421
2422                            if (tooltipText) {
2423                                tooltipText += '<br />';
2424                            }
2425
2426                            if (item.series.label) {
2427                                tooltipText += item.series.label;
2428                            }
2429
2430                            if (item.series.chartData) {
2431                                var pointValue = item.series.chartData[item.dataIndex].YValueFormatted || item.series.chartData[item.dataIndex].YValue || item.series.chartData[item.dataIndex].YValueTotal || '';
2432
2433                                tooltipText += ': ' + pointValue;
2434
2435                                if (item.series.chartData[item.dataIndex].Note) {
2436                                    tooltipText += '<br />' + item.series.chartData[item.dataIndex].Note;
2437                                }
2438                            }
2439                        }
2440
2441                        $toolTip.find('.tooltip-inner').html(tooltipText);
2442
2443                        var tipTop = pos.pageY - $toolTip.height() - 10;
2444
2445                        var windowWidth = $(window).width();
2446                        var tooltipWidth = $toolTip.width();
2447                        var tipLeft = pos.pageX - ( tooltipWidth / 2);
2448                        if (tipLeft + tooltipWidth + 10 >= windowWidth) {
2449                            tipLeft = tipLeft - (tooltipWidth / 2);
2450                            $toolTip.removeClass("top");
2451                            $toolTip.addClass("left");
2452                        }
2453                        else {
2454                            $toolTip.removeClass("left");
2455                            $toolTip.addClass("top");
2456                        }
2457
2458                        $toolTip.css({ top: tipTop, left: tipLeft, opacity: 1 });
2459                        $toolTip.show();
2460                    }
2461                    else {
2462                        $toolTip.hide();
2463                    }
2464                });
2465
2466            }
2467        };
2468
2469        return exports;
2470    }());
2471}(jQuery));
2472
2473
2474;
2475(function ($) {
2476    'use strict';
2477    window.Rock = window.Rock || {};
2478    Rock.controls = Rock.controls || {};
2479
2480    Rock.controls.colorSelector = (function () {
2481
2482        // #region Utilities
2483
2484        var ColorUtilities = (function () {
2485            /**
2486             * Detects if two RGB colors are similar.
2487             * @param r1 {number} Red 1 color (0-255).
2488             * @param g1 {number} Green 1 color (0-255).
2489             * @param b1 {number} Blue 1 color (0-255).
2490             * @param r2 {number} Red 2 color (0-255).
2491             * @param g2 {number} Green 2 color (0-255).
2492             * @param b2 {number} Blue 2 color (0-255).
2493             * @param percentSimilar {number|undefined} The percent (0 - 1) that determines if the colors are similar. 
2494             * @returns {Boolean} `true` if the numbers are similar; otherwise, `false`.
2495             */
2496            function areColorsSimilar(r1, g1, b1, r2, g2, b2, percentSimilar) {
2497                if (typeof percentSimilar === "undefined") {
2498                    percentSimilar = 0.99;
2499                }
2500
2501                var actualDistance = getColorDistance(r1, g1, b1, r2, g2, b2);
2502
2503                var actualPercent = (maxLumaDistance - actualDistance) / maxLumaDistance;
2504
2505                // Use the specified percent to determine if the colors are similar.
2506                return actualPercent >= percentSimilar;
2507            }
2508
2509            /**
2510             * Gets the distance between two RGB colors.
2511             * @param r1 {number} Red 1 color (0-255).
2512             * @param g1 {number} Green 1 color (0-255).
2513             * @param b1 {number} Blue 1 color (0-255).
2514             * @param r2 {number} Red 2 color (0-255).
2515             * @param g2 {number} Green 2 color (0-255).
2516             * @param b2 {number} Blue 2 color (0-255).
2517             * @returns {number} The distance between two RGB colors.
2518             */
2519            function getColorDistance(r1, g1, b1, r2, g2, b2) {
2520                var redDiffSquared = Math.pow(r2 - r1, 2);
2521                var greenDiffSquared = Math.pow(g2 - g1, 2);
2522                var blueDiffSquared = Math.pow(b2 - b1, 2);
2523                var redAverage = (r2 + r1) / 2;
2524
2525                return Math.sqrt(
2526                    (2 * redDiffSquared) +
2527                    (4 * greenDiffSquared) +
2528                    (3 * blueDiffSquared) +
2529                    (redAverage * (redDiffSquared - blueDiffSquared) / 256)
2530                );
2531            }
2532
2533            /**
2534             * Converts a hex color (#FFFFFF) to an RGB color. 
2535             * @param {string} hexValue The hex color to convert.
2536             * @returns {Number[]} The converted RGB color.
2537             */
2538            function hexToRgba(hexValue) {
2539                var hex3Or4 = /^#[0-9a-fA-F]{3}([0-9a-fA-F])?$/;
2540                if (hex3Or4.test(hexValue)) {
2541                    var rHex = hexValue[1];
2542                    var gHex = hexValue[2];
2543                    var bHex = hexValue[3];
2544                    var aHex = hexValue[4] || "f";
2545                    // A 3-digit hex color #123 is equivalent to the 6-digit hex color #112233,
2546                    // where each color channel (r, g, and b) is repeated.
2547                    return [
2548                        parseInt(rHex + rHex, 16),
2549                        parseInt(gHex + gHex, 16),
2550                        parseInt(bHex + bHex, 16),
2551                        parseInt(aHex + aHex, 16)
2552                    ];
2553                }
2554
2555                var hex6Or8 = /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/;
2556                if (hex6Or8.test(hexValue)) {
2557                    var rHex = hexValue.substring(1, 3);
2558                    var gHex = hexValue.substring(3, 5);
2559                    var bHex = hexValue.substring(5, 7);
2560                    var aHex = hexValue.substring(7) || "ff";
2561                    return [
2562                        parseInt(rHex, 16),
2563                        parseInt(gHex, 16),
2564                        parseInt(bHex, 16),
2565                        Math.round(parseInt(aHex, 16) / 255)
2566                    ];
2567                }
2568
2569                // Return transparent if not a valid hex color.
2570                return [0, 0, 0, 0];
2571            }
2572
2573            /**
2574             * Returns the perceived brightness of an RGB color.
2575             * @param r {number} Red color (0-255).
2576             * @param g {number} Green color (0-255).
2577             * @param b {number} Blue color (0-255).
2578             * @returns {Number[]}
2579             */
2580            function rgbToLuma(r, g, b) {
2581                /**
2582                 * To get a color's perceived brightness,
2583                 * each color component is multiplied by a different constant.
2584                 * The constants are weighted differently based on the perceived brightness by the human eye
2585                 * --green is perceived as the brightest color, then red, then blue (they all add up to 1).
2586                 *
2587                 * The result will be a color in the color space:
2588                 * [0, 0, 0] to [0.2126 * 255, 0.7152 * 255, 0.0722 * 255]
2589                 * or...
2590                 * [0, 0, 0] to [54.213, 182.376, 18.411]
2591                 */
2592
2593                return [0.2126 * r, 0.7152 * g, 0.0722 * b];
2594            }
2595
2596            /**
2597             * Returns the RGBA number array from a CSS "rgba(r, g, b, a)" string.
2598             * @param rgbFunc {string} The "rgb(r, g, b)" string.
2599             * @returns {Number[]} An RGBA array [r, b, b, a].
2600             */
2601            function rgbFuncToRgba(rgbFunc) {
2602                var rgb = /^rgb[a]?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)\)$/;
2603                var rgbMatches = rgbFunc.match(rgb);
2604                if (rgbMatches && rgbMatches.length) {
2605                    return [
2606                        parseInt(rgbMatches[1]),
2607                        parseInt(rgbMatches[2]),
2608                        parseInt(rgbMatches[3]),
2609                        1
2610                    ];
2611                }
2612
2613                var rgba = /^rgb[a]?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d*(\.\d+)?)\)$/;
2614                var rgbaMatches = rgbFunc.match(rgba);
2615                if (rgbaMatches && rgbMatches.length) {
2616                    return [
2617                        parseInt(rgbaMatches[1]),
2618                        parseInt(rgbaMatches[2]),
2619                        parseInt(rgbaMatches[3]),
2620                        Number(rgbaMatches[4])
2621                    ];
2622                }
2623
2624                // Return transparent by default.
2625                return [0, 0, 0, 0];
2626            }
2627
2628            return {
2629                areColorsSimilar,
2630                getColorDistance,
2631                hexToRgba,
2632                rgbToLuma,
2633                rgbFuncToRgba
2634            }
2635        })();
2636
2637        // #endregion
2638
2639        // #region Types
2640
2641        /**
2642         * The ColorSelector initialization options.
2643         * @typedef {Object} ColorSelectorInitOptions
2644         * @property {string} controlId The control ID for the color selector (without leading #).
2645         * @property {boolean} allowMultiple When true, the Color Selector will behave like a checkbox list; otherwise, it will behave like a radiobutton list.
2646         */
2647
2648        // #endregion 
2649
2650        // #region Constants
2651
2652        var colorSimilarityPercentage = Object.freeze(0.95);
2653        var maxLuma = Object.freeze(ColorUtilities.rgbToLuma(255, 255, 255));
2654        var maxLumaDistance = Object.freeze(ColorUtilities.getColorDistance(maxLuma[0], maxLuma[1], maxLuma[2], 0, 0, 0));
2655        var camouflagedLightClass = Object.freeze("camouflaged-light");
2656        var camouflagedDarkClass = Object.freeze("camouflaged-dark");
2657
2658        // #endregion
2659
2660        // #region Classes
2661
2662        // #region Constructor
2663
2664        /**
2665         * Creates a new instance of the ColorSelector class.
2666         * @param {ColorSelectorInitOptions} options
2667         */
2668        function ColorSelector(options) {
2669            if (!options.controlId) {
2670                throw "`controlId` is required.";
2671            }
2672
2673            this.allowMultiple = !!options.allowMultiple;
2674            this.controlId = options.controlId;
2675
2676            // Set JQuery selectors.
2677            this.$colorSelector = $("#" + this.controlId);
2678            this.$colorSelectorItemContainers = this.$colorSelector.children(".checkbox");
2679
2680            // Set defaults for background color and perceived brightness.
2681            // These will be overwritten during initialization.
2682            this.backgroundColor = [255, 255, 255];
2683            this.backgroundLuma = maxLuma;
2684        }
2685
2686        // #endregion
2687
2688        // #region Methods
2689
2690        /**
2691         * Initializes the color selector.
2692         */
2693        ColorSelector.prototype.initialize = function () {
2694            var colorSelectorElement = this.$colorSelector[0];
2695
2696            if (!colorSelectorElement) {
2697                throw "Unable to find color selector element with control ID " + this.controlId;
2698            }
2699
2700            // Set the background color of the color selector.
2701            this.backgroundColor = getInheritedBackgroundColor(colorSelectorElement);
2702
2703            // Set the perceived brightness of the color picker.
2704            // The perceived brightness is better for determining
2705            // when two colors are similar to the human eye
2706            // instead of comparing RGB values.
2707            this.backgroundLuma = ColorUtilities.rgbToLuma(this.backgroundColor[0], this.backgroundColor[1], this.backgroundColor[2]);
2708
2709            var $colorSelectorItemContainers = this.$colorSelectorItemContainers;
2710
2711            // Initialize the color items.
2712            var checkedFound = false;
2713            for (var i = 0; i < $colorSelectorItemContainers.length; i++) {
2714                var $colorSelectorItemContainer = $($colorSelectorItemContainers[i]);
2715
2716                // If only one selection is allowed, then only leave the first checked input as checked,
2717                // and uncheck the others.
2718                if (!this.allowMultiple) {
2719                    var $input = findInput($colorSelectorItemContainer);
2720
2721                    if ($input.prop("checked")) {
2722                        if (checkedFound) {
2723                            // A checked input was already found so mark this input as unchecked.
2724                            $input.prop("checked", false);
2725                        }
2726
2727                        checkedFound = true;
2728                    }
2729                }
2730
2731                initializeItem(this, $colorSelectorItemContainer);
2732            }
2733        };
2734
2735        // #endregion
2736
2737        // #region Private Functions
2738
2739        /**
2740         * Finds the checkbox input for a color selector item container.
2741         * @param {JQuery<HTMLElement>} $colorSelectorItemContainer
2742         * @returns {JQuery<HTMLInputElement>}
2743         */
2744        function findInput($colorSelectorItemContainer) {
2745            return $colorSelectorItemContainer.find('input[type="checkbox"]');
2746        }
2747
2748        /**
2749         * Initializes the color items.
2750         * @param {ColorSelector} colorSelector
2751         * @param {JQuery<HTMLElement>} $colorSelectorItemContainer
2752         */
2753        function initializeItem(colorSelector, $colorSelectorItemContainer) {
2754            $colorSelectorItemContainer.addClass("color-selector-item-container");
2755
2756            var $checkboxLabel = $colorSelectorItemContainer.find("label");
2757            $checkboxLabel.addClass("color-selector-item");
2758
2759            var $checkboxInput = findInput($colorSelectorItemContainer);
2760            $checkboxInput.addClass("color-selector-item-checkbox");
2761
2762            styleInput($checkboxInput);
2763
2764            // Update the checked state whenever it changes.
2765            $checkboxInput.on("click", function () {
2766                onCheckboxCheckChanged(colorSelector, $(this));
2767            });
2768
2769            // Remove the .checkbox CSS class as we don't want any checkbox styling.
2770            $colorSelectorItemContainer.removeClass("checkbox");
2771
2772            // Set the label background color to the color found in the label text.
2773            var $labelText = $checkboxLabel.find(".label-text");
2774            var itemHexColor = $labelText.text();
2775            $checkboxLabel.css("background-color", itemHexColor);
2776
2777            // Remove the label text so just the color is displayed.
2778            $labelText.remove();
2779
2780            // Determine if the item needs a border to separate it from the background.
2781            var itemRgb = ColorUtilities.hexToRgba(itemHexColor);
2782            var itemLuma = ColorUtilities.rgbToLuma(itemRgb[0], itemRgb[1], itemRgb[2]);
2783
2784            if (ColorUtilities.areColorsSimilar(colorSelector.backgroundLuma[0], colorSelector.backgroundLuma[1], colorSelector.backgroundLuma[2], itemLuma[0], itemLuma[1], itemLuma[2], colorSimilarityPercentage)) {
2785                $checkboxLabel.addClass(getCamouflagedClass(itemLuma[0], itemLuma[1], itemLuma[2]));
2786            }
2787        }
2788
2789        /**
2790         * Handles the color item checkbox changed event.
2791         * @param {ColorSelector} colorSelector The color selector.
2792         * @param {JQuery<HTMLInputElement>} $checkboxInput The checkbox input.
2793         */
2794        function onCheckboxCheckChanged(colorSelector, $checkboxInput) {
2795            styleInput($checkboxInput);
2796
2797            if (!colorSelector.allowMultiple) {
2798                // For single select (radio) bevahior, clicking a checkbox should always check it.
2799                // If the checkbox is already checked, then return false to prevent the default behavior of unchecking it.
2800
2801                // Uncheck all other checkboxes.
2802                var $colorSelectorItemContainers = colorSelector.$colorSelectorItemContainers;
2803
2804                for (var i = 0; i < $colorSelectorItemContainers.length; i++) {
2805                    var $otherItemInput = findInput($($colorSelectorItemContainers[i]));
2806
2807                    if ($otherItemInput[0] !== $checkboxInput[0]) {
2808                        $otherItemInput.prop("checked", false);
2809                        styleInput($otherItemInput);
2810                    }
2811                }
2812            }
2813        }
2814
2815        /**
2816         * 
2817         * @param {JQuery<HTMLInputElement>} $input
2818         * @param {JQuery<HTMLElement | undefined>} $label
2819         * @param {JQuery<HTMLElement | undefined} $container
2820         */
2821        function styleInput($input, $label, $container) {
2822            if (!$label) {
2823                $label = $input.parent("label");
2824            }
2825
2826            if (!$container) {
2827                $container = $input.closest(".color-selector-item-container");
2828            }
2829
2830            if ($input.prop("checked")) {
2831                $label.addClass("checked");
2832                $container.addClass("checked");
2833            }
2834            else {
2835                $label.removeClass("checked");
2836                $container.removeClass("checked");
2837            }
2838        }
2839
2840        /**
2841         * Gets the camouflaged CSS class for a luma color (rL, gL, bL).
2842         * @param {number} rLuma The R luma color.
2843         * @param {number} gLuma The G luma color.
2844         * @param {number} bLuma The B luma color.
2845         * @returns {string} The camouflaged CSS class for a luma color.
2846         */
2847        function getCamouflagedClass(rLuma, gLuma, bLuma) {
2848            var luma = rLuma + gLuma + bLuma;
2849            if (luma > 128) { // Max is 255
2850                return camouflagedLightClass;
2851            }
2852            else {
2853                return camouflagedDarkClass;
2854            }
2855        }
2856
2857        /**
2858         * Gets the first RGBA background color that isn't the default,
2859         * starting from the element and working its way up the
2860         * parent element chain.
2861         * @param {Element} el
2862         * @returns {Number[]} The first RGBA background color that isn't the default; otherwise, the default color is returned.
2863         */
2864        function getInheritedBackgroundColor(el) {
2865
2866            function getDefaultBackground() {
2867                var div = document.createElement("div");
2868                document.head.appendChild(div);
2869                var bg = window.getComputedStyle(div).backgroundColor;
2870                document.head.removeChild(div);
2871                return bg;
2872            }
2873
2874            // Get default style for current browser.
2875            var defaultStyle = getDefaultBackground(); // typically "rgba(0, 0, 0, 0)"
2876
2877            // Start with the supplied element.
2878            var elementsToProcess = [
2879                el
2880            ];
2881
2882            while (elementsToProcess.length) {
2883                var elementToProcess = elementsToProcess.shift();
2884
2885                if (!elementToProcess) {
2886                    // Skip to the next element if the current one is nullish.
2887                    continue;
2888                }
2889
2890                var backgroundColor = window.getComputedStyle(elementToProcess).backgroundColor;
2891
2892                // If we got a real value, return it.
2893                if (backgroundColor != defaultStyle) {
2894                    return ColorUtilities.rgbFuncToRgba(backgroundColor);
2895                }
2896                // Otherwise, process the next parent element.
2897                else if (elementToProcess.parentElement) {
2898                    elementsToProcess.push(elementToProcess.parentElement);
2899                }
2900            }
2901
2902            // If we have reached the top parent element without getting an explicit color,
2903            // return the default style.
2904            return ColorUtilities.rgbFuncToRgba(defaultStyle);
2905        }
2906
2907        // #endregion
2908
2909        // #endregion
2910
2911        // Keeps track of ColorSelector instances. 
2912        var colorSelectors = {};
2913
2914        var exports = {
2915            /**
2916             * Initializes a new ColorSelector instance.
2917             * @param {ColorSelectorInitOptions} options
2918             * @returns {ColorSelector}
2919             */
2920            initialize: function (options) {
2921                var cs = new ColorSelector(options);
2922                cs.initialize();
2923
2924                colorSelectors[options.controlId] = cs;
2925
2926                return cs;
2927            },
2928
2929            /**
2930             * Gets a ColorSelector instance by control ID.
2931             * @param {string} controlId The control ID.
2932             * @returns {ColorSelector}
2933             */
2934            get: function(controlId) {
2935                return colorSelectors[controlId];
2936            },
2937
2938            /**
2939             * Sets the camouflaged class if the element color is similar to the target element (or parent if not set).
2940             * @param {string} elementSelector The selector of the element to compare.
2941             * @param {string} targetSelector (Optional) The selector of the target element with which to compare colors. Defaults to the parent element.
2942             */
2943            setCamouflagedClass: function (elementSelector, targetSelector) {
2944                var element = document.querySelector(elementSelector);
2945                var target = targetSelector ? document.querySelector(target
2945Selector) : element.parentElement;
2946
2947                var elementColor = getInheritedBackgroundColor(element);
2948                var targetColor = getInheritedBackgroundColor(target);
2949
2950                var elementLuma = ColorUtilities.rgbToLuma(elementColor[0], elementColor[1], elementColor[2]);
2951                var targetLuma = ColorUtilities.rgbToLuma(targetColor[0], targetColor[1], targetColor[2]);
2952
2953                var isSamePerceivedBrightness = (
2954                    elementLuma[0] === targetLuma[0]
2955                    && elementLuma[1] === targetLuma[1]
2956                    && elementLuma[2] === targetLuma[2]
2957                );
2958
2959                // Short-circuit if elements have the same color.
2960                if (isSamePerceivedBrightness
2961                    || ColorUtilities.areColorsSimilar(elementLuma[0], elementLuma[1], elementLuma[2], targetLuma[0], targetLuma[1], targetLuma[2], colorSimilarityPercentage)) {
2962                    element.classList.add(getCamouflagedClass(elementLuma[0], elementLuma[1], elementLuma[2]));
2963                }
2964                else {
2965                    element.classList.remove(camouflagedLightClass);
2966                    element.classList.remove(camouflagedDarkClass);
2967                }
2968            }
2969        };
2970
2971        return exports;
2972    }());
2973}(jQuery));
2974;
2975(function ($)
2976{
2977  'use strict';
2978  window.Rock = window.Rock || {};
2979  Rock.controls = Rock.controls || {};
2980
2981  Rock.controls.datePartsPicker = (function ()
2982  {
2983    var exports = {
2984      initialize: function (options)
2985      {
2986        if (!options.id) {
2987          throw 'id is required';
2988        }
2989
2990      },
2991      clientValidate: function (validator, args)
2992      {
2993        var $datePartsPicker = $(validator).closest('.js-datepartspicker');
2994        var monthNumber = Number($datePartsPicker.find('.js-month').val());
2995        var dayNumber = Number($datePartsPicker.find('.js-day').val());
2996        var yearNumber = Number($datePartsPicker.find('.js-year').val());
2997        var required = $datePartsPicker.attr('data-required') == 'true';
2998        var requireYear = $datePartsPicker.attr('data-requireyear') == 'true';
2999        var allowFuture = $datePartsPicker.attr('data-allowFuture') == 'true';
3000        var itemLabelText = $datePartsPicker.attr('data-itemlabel');
3001
3002        var isValid = true;
3003
3004        if (!allowFuture) {
3005          if (monthNumber && dayNumber && yearNumber) {
3006            // NOTE: Javascript Date Contructor's month parameter is zero based!
3007            // see http://stackoverflow.com/questions/2552483/why-does-the-month-argument-range-from-0-to-11-in-javascripts-date-constructor
3008            var bDate = new Date(yearNumber, monthNumber-1, dayNumber);
3009            var now = new Date();
3010            if (bDate > now) {
3011              isValid = false;
3012              validator.errormessage = itemLabelText + ' cannot be a future date.'
3013            }
3014          }
3015        }
3016
3017        if (monthNumber && dayNumber && (yearNumber || !requireYear)) {
3018          // month, day and (conditionally) year are all set, it's OK
3019        }
3020        else if (monthNumber || dayNumber || yearNumber) {
3021          // at least one of them is set, but some are not, so it is invalid
3022          isValid = false;
3023          validator.errormessage = itemLabelText + ' must be a valid value.';
3024        }
3025        else if (required) {
3026          // nothing is set but it is a required field.
3027          isValid = false;
3028          validator.errormessage = itemLabelText + ' is required.';
3029        }
3030
3031        var control = $datePartsPicker
3032        if (isValid) {
3033          control.removeClass('has-error');
3034        } else {
3035          control.addClass('has-error');
3036        }
3037
3038        args.IsValid = isValid;
3039      }
3040    };
3041
3042    return exports;
3043  }());
3044}(jQuery));
3045;
3046(function ($) {
3047    'use strict';
3048    window.Rock = window.Rock || {};
3049    Rock.controls = Rock.controls || {};
3050
3051    Rock.controls.datePicker = (function () {
3052        var exports = {
3053            initialize: function (options) {
3054                if (!options.id) {
3055                    throw 'id is required';
3056                }
3057                var dateFormat = 'mm/dd/yyyy';
3058                if (options.format) {
3059                    dateFormat = options.format;
3060                }
3061
3062                var $textBox = $('#' + options.id);
3063
3064                var $datePickerContainer = $textBox.closest('.js-date-picker-container');
3065                var $datePickerInputGroup = $textBox.closest('.input-group.js-date-picker');
3066
3067                // uses https://github.com/uxsolutions/bootstrap-datepicker
3068                var datePicker = $datePickerInputGroup.datepicker({
3069                    format: dateFormat,
3070                    assumeNearbyYear: 10,
3071                    autoclose: true,
3072                    container: options.container || document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || "body",
3073                    orientation: options.orientation || "auto",
3074                    todayBtn: "linked",
3075                    forceParse: options.forceParse,
3076                    startDate: options.startDate,
3077                    endDate: options.endDate || new Date(8640000000000000),
3078                    startView: options.startView,
3079                    showOnFocus: options.showOnFocus,
3080                    todayHighlight: options.todayHighlight,
3081                    zIndexOffset: 1050
3082                });
3083
3084                // Listen to actions from external code.
3085                if (options.actions) {
3086                    // Update the bootstrap datepicker when external code sets the date.
3087                    options.actions.onSetLocalDate = (localDate) => {
3088                        $datePickerInputGroup.data("datepicker")
3089                            .setDate(localDate) // Set the date.
3090                            .fill();            // Update the UI.
3091                    };
3092                }
3093
3094                // note: using 'change' instead of datePicker's 'changeDate' so that both manual entry and picking from calender works
3095                datePicker.on('change', function (e) {
3096                    if (options.postbackScript) {
3097                        window.location = "javascript:" + options.postbackScript;
3098                    }
3099
3100                    if (options.onChangeScript) {
3101                        options.onChangeScript();
3102                    }
3103                });
3104
3105                // if the guest clicks the addon select all the text in the input
3106                $datePickerInputGroup.find('.input-group-addon').on('click', function () {
3107                    $(this).siblings('.form-control').select();
3108                });
3109
3110                $datePickerContainer.find('.js-current-date-checkbox').on('click', function (a, b, c) {
3111                    var $dateOffsetBox = $datePickerContainer.find('.js-current-date-offset');
3112                    var $dateOffsetlabel = $("label[for='" + $dateOffsetBox.attr('id') + "']")
3113                    if ($(this).is(':checked')) {
3114                        $dateOffsetlabel.show();
3115                        $dateOffsetBox.show();
3116
3117                        // set textbox val to something instead of empty string so that validation doesn't complain
3118                        $textBox.data("last-value", $textBox.val()).val('Current').prop('disabled', true).addClass('aspNetDisabled');
3119
3120                    } else {
3121                        $dateOffsetlabel.hide();
3122                        $dateOffsetBox.hide();
3123
3124                        // set textbox val to last value so that validation will work again (if it is enabled)
3125                        $textBox.val($textBox.data('last-value')).prop('disabled', false).removeClass('aspNetDisabled');
3126                    }
3127                });
3128            }
3129        };
3130
3131        return exports;
3132    }());
3133}(jQuery));
3134;
3135(function ($)
3136{
3137  'use strict';
3138  window.Rock = window.Rock || {};
3139  Rock.controls = Rock.controls || {};
3140
3141  Rock.controls.dateRangePicker = (function ()
3142  {
3143    var exports = {
3144      initialize: function (options)
3145      {
3146        if (!options.id) {
3147          throw 'id is required';
3148        }
3149      },
3150      clientValidate: function (validator, args)
3151      {
3152        var $dateRangePicker = $(validator).closest('.js-daterangepicker');
3153        var lowerValue = $dateRangePicker.find('.js-lower input').val();
3154        var upperValue = $dateRangePicker.find('.js-upper input').val();
3155        var required = $dateRangePicker.attr('data-required') == 'true';
3156        var itemLabelText = $dateRangePicker.attr('data-itemlabel');
3157
3158        var isValid = true;
3159
3160        if (required) {
3161            // if required, then make sure that the date range has a start and/or end date (can't both be blank)
3162            if (lowerValue.length == 0 && upperValue.length == 0) {
3163                isValid = false;
3164                validator.errormessage = itemLabelText + " is required";
3165            }
3166        }
3167
3168        var control = $dateRangePicker;
3169        if (isValid)
3170        {
3171          control.removeClass('has-error');
3172        }
3173        else
3174        {
3175          control.addClass('has-error');
3176        }
3177
3178        args.IsValid = isValid;
3179      }
3180    };
3181
3182    return exports;
3183  }());
3184}(jQuery));
3185;
3186(function ($) {
3187    'use strict';
3188    window.Rock = window.Rock || {};
3189    Rock.controls = Rock.controls || {};
3190
3191    Rock.controls.dateTimePicker = (function () {
3192        var exports = {
3193            initialize: function (options) {
3194                if (!options.id) {
3195                    throw 'id is required';
3196                }
3197                var dateFormat = 'mm/dd/yyyy';
3198                if (options.format) {
3199                    dateFormat = options.format;
3200                }
3201
3202                var $dp = $('#' + options.id + " .js-datetime-date");
3203
3204                var $dateTimePickerContainer = $dp.closest('.js-datetime-picker-container');
3205                var $dateTimePickerInputGroup = $dp.closest('.input-group.date');
3206
3207                // uses https://github.com/uxsolutions/bootstrap-datepicker
3208                $dateTimePickerInputGroup.datepicker({
3209                    format: dateFormat,
3210                    assumeNearbyYear: 10,
3211                    autoclose: true,
3212                    todayBtn: "linked",
3213                    startView: options.startView || 'month',
3214                    todayHighlight: options.todayHighlight || true,
3215                    zIndexOffset: 1050
3216                });
3217
3218                // if the guest clicks the addon select all the text in the input
3219                $dateTimePickerInputGroup.find('.input-group-addon').on('click', function ()
3220                {
3221                  $(this).siblings('.form-control').select();
3222                });
3223
3224                var $tp = $('#' + options.id + " .js-datetime-time");
3225                if ($tp) {
3226                    var $tpid = $tp.attr('id');
3227                    if ($tpid) {
3228                        Rock.controls.timePicker.initialize({
3229                            id: $tpid
3230                        });
3231                    }
3232                }
3233
3234                $dateTimePickerContainer.find('.js-current-datetime-checkbox').on('click', function (a, b, c) {
3235                    var $dateTimeOffsetBox = $dateTimePickerContainer.find('.js-current-datetime-offset');
3236                    var $dateOffsetlabel = $("label[for='" + $dateTimeOffsetBox.attr('id') + "']")
3237                    if ($(this).is(':checked')) {
3238                        $dateOffsetlabel.removeClass('aspNetDisabled').show();
3239                        $dateTimeOffsetBox.show();
3240                        $dateTimeOffsetBox.prop('disabled', false).removeClass('aspNetDisabled').val( $dateTimeOffsetBox.data('last-value') );
3241                        $dp.data( "last-value", $dp.val()).val('').prop('disabled', true).addClass('aspNetDisabled').prop('placeholder', 'Current');
3242                        $tp.data( "last-value", $tp.val()).val('').prop('disabled', true).addClass('aspNetDisabled');
3243                    } else {
3244                        $dateOffsetlabel.addClass('aspNetDisabled').hide();
3245                        $dateTimeOffsetBox.data( "last-value", $dateTimeOffsetBox.val()).hide();
3246                        $dateTimeOffsetBox.val('').prop('disabled', true).addClass('aspNetDisabled');
3247                        $dp.prop('disabled', false).removeClass('aspNetDisabled').prop('placeholder', '').val($dp.data('last-value'));
3248                        $tp.prop('disabled', false).removeClass('aspNetDisabled').val($tp.data('last-value'));
3249                    }
3250                });
3251            }
3252        };
3253
3254        return exports;
3255    }());
3256}(jQuery));
3257;
3258(function ($) {
3259    'use strict';
3260    window.Rock = window.Rock || {};
3261    Rock.controls = Rock.controls || {};
3262
3263    Rock.controls.electronicSignatureControl = (function () {
3264        var exports = {
3265            /**
3266             * 
3267             * @param {any} options
3268             */
3269            initialize: function (options) {
3270                if (!options.controlId) {
3271                    throw 'id is required';
3272                }
3273
3274                var self = this;
3275
3276                self.ImageMimeType = options.imageMimeType || "image/png";
3277
3278                var $control = $('#' + options.controlId);
3279
3280                if ($control.length == 0) {
3281                    return;
3282                }
3283
3284                self.$signatureControl = $control;
3285                self.$signatureTyped = $('.js-signature-typed', $control);
3286
3287                self.$signatureEntryDrawn = $('.js-signature-entry-drawn', $control);
3288                self.$signaturePadCanvas = $('.js-signature-pad-canvas', $control);
3289                self.$signatureImageDataURL = $('.js-signature-data', $control);
3290                
3291                self.$clearSignature = $('.js-clear-signature', $control);
3292                self.$saveSignature = $('.js-save-signature', $control);
3293
3294                self.initializeSignaturePad();
3295
3296                self.$saveSignature.click(function () {
3297                    if (!self.signatureIsValid(self.$signatureControl)) {
3298                        return false;
3299                    }
3300
3301                    if (self.$signaturePadCanvas.length) {
3302                        var signaturePad = self.$signaturePadCanvas.data('signatureComponent');
3303
3304                        if (signaturePad) {
3305                            var signatureImageDataUrl = signaturePad.toDataURL(self.ImageMimeType);
3306                            self.$signatureImageDataURL.val(signatureImageDataUrl);
3307                        }
3308                    }
3309                });
3310            },
3311
3312            /** */
3313            initializeSignaturePad: function () {
3314                var self = this;
3315                if (!self.$signaturePadCanvas.length) {
3316                    return
3317                }
3318
3319                var signaturePadOptions = {};
3320                if (self.ImageMimeType == "image/jpeg") {
3321                    // NOTE That if we use jpeg, we'll have to deal with this https://github.com/szimek/signature_pad/issues/584
3322                    signaturePadOptions = {
3323                        penColor: 'black',
3324                        backgroundColor: 'white'
3325                    }
3326                };
3327
3328                var signaturePad = new SignaturePad(self.$signaturePadCanvas[ 0 ], signaturePadOptions);
3329
3330                self.$signaturePadCanvas.data('signatureComponent', signaturePad);
3331
3332                self.$clearSignature.click(function () {
3333                    signaturePad.clear();
3334                })
3335
3336                window.addEventListener("resize", function () {
3337                    self.resizeSignatureCanvas(self)
3338                });
3339
3340                self.resizeSignatureCanvas(self);
3341            },
3342
3343            /** */
3344            resizeSignatureCanvas: function (signatureControl) {
3345                if (!signatureControl.$signaturePadCanvas.length) {
3346                    return;
3347                }
3348
3349                // If the window is resized, that'll affect the drawing canvas
3350                // also, if there is an existing signature, it'll get messed up, so clear it and
3351                // make them sign it again. See additional details why 
3352                // https://github.com/szimek/signature_pad
3353                var signaturePadCanvas = signatureControl.$signaturePadCanvas[ 0 ];
3354                var containerWidth = signatureControl.$signatureEntryDrawn.width();
3355                if (!containerWidth || containerWidth == 0) {
3356                    containerWidth = 400;
3357                }
3358
3359                // Note the suggestion  https://github.com/szimek/signature_pad#handling-high-dpi-screens
3360                // to re-calculate the ratio based on window.devicePixelRatio isn't needed. 
3361                // We can just use the width() of the container and use fixed height of 100.
3362                const ratio = 1;
3363                signaturePadCanvas.width = containerWidth * ratio;
3364                signaturePadCanvas.height = 100 * ratio;
3365                signaturePadCanvas.getContext("2d").scale(ratio, ratio);
3366
3367                var signaturePad = signatureControl.$signaturePadCanvas.data('signatureComponent');
3368                signaturePad.clear();
3369            },
3370
3371            signatureIsValid: function ($signatureControl) {
3372                var $signatureCanvas = $signatureControl.find('.js-signature-pad-canvas')
3373                var $signatureTyped = $signatureControl.find('.js-signature-typed');
3374
3375                if ($signatureCanvas.length == 0 && $signatureTyped.length == 0) {
3376                    return;
3377                }
3378
3379                var isValid = true;
3380
3381                if ($signatureCanvas.length) {
3382                    var signatureCanvas = $signatureCanvas.data('signatureComponent');
3383
3384                    if (signatureCanvas.isEmpty()) {
3385                        isValid = false;
3386                    }
3387                }
3388                else {
3389                    if ($signatureTyped.val().trim() == '') {
3390                        isValid = false;
3391                    }
3392                }
3393
3394                return isValid;
3395            },
3396
3397            /** */
3398            clientValidate: function (validator, args) {
3399                var $signatureControl = $(validator).closest('.js-electronic-signature-control');
3400
3401                var isValid = this.signatureIsValid($signatureControl);
3402                
3403                if (!isValid) {
3404                    validator.errormessage = "Signature is required.";
3405                }
3406
3407                args.IsValid = isValid;
3408            }
3409        };
3410
3411        return exports;
3412    }());
3413}(jQuery));
3414;
3415(function (Sys) {
3416    'use strict';
3417    Sys.Application.add_load(function () {
3418
3419        var preventDoublePostback = function () {
3420            var inputOnChange = $(this).attr('onchange');
3421            var inputOnClick = $(this).attr('onclick');
3422            if (inputOnChange && inputOnChange.indexOf('__doPostBack') > 0 || inputOnClick && inputOnClick.indexOf('__doPostBack') > 0) {
3423                var $blockButtons = $(this).closest('.js-block-instance').find('a');
3424                $blockButtons.attr('disabled', 'disabled')
3425                    .prop('disabled', true)
3426                    .attr('disabled', 'disabled')
3427                    .addClass('disabled');
3428            }
3429        };
3430
3431        // since some of the edit controls do a postback to show/hide other controls, we want to disable buttons to prevent a double postback
3432        $('select.js-prevent-double-postback,input.js-prevent-double-postback').on('change', preventDoublePostback);
3433        $('.js-prevent-double-postback input[type="radio"]').on('click', preventDoublePostback);
3434    });
3435}(Sys));
3436;
3437(function ($) {
3438    'use strict';
3439    window.Rock = window.Rock || {};
3440    Rock.controls = Rock.controls || {};
3441
3442    Rock.controls.fileUploader = (function () {
3443        var _configure = function (options) {
3444            options.isBinaryFile = options.isBinaryFile || 'T';
3445            options.uploadUrl = options.uploadUrl || 'FileUploader.ashx';
3446
3447            var wsUrl = Rock.settings.get('baseUrl')
3448                        + options.uploadUrl + '?'
3449                        + 'isBinaryFile=' + options.isBinaryFile;
3450
3451            if (options.isBinaryFile === 'T') {
3452                wsUrl += '&fileId=' + options.fileId
3453                    + '&fileTypeGuid=' + options.fileTypeGuid;
3454            }
3455            else {
3456                // note rootFolder is encrypted to prevent direct access to filesystem via the URL
3457                wsUrl += '&rootFolder=' + (encodeURIComponent(options.rootFolder) || '');
3458            }
3459
3460            if (options.isTemporary === 'F') {
3461              wsUrl += '&IsTemporary=False';
3462            }
3463
3464            if (options.parentEntityTypeId !== 'null') {
3465                wsUrl += '&ParentEntityTypeId=' + options.parentEntityTypeId;
3466            }
3467
3468            if (options.parentEntityId !== 'null') {
3469                wsUrl += '&ParentEntityId=' + options.parentEntityId;
3470            }
3471
3472            // uses https://github.com/blueimp/jQuery-File-Upload
3473            $('#' + options.controlId).fileupload({
3474                url: wsUrl,
3475                dataType: 'json',
3476                dropZone: $('#' + options.controlId).closest('.fileupload-dropzone'),
3477                autoUpload: true,
3478                submit: options.submitFunction,
3479                sequentialUploads: true,
3480                start: function (e, data) {
3481                    var $el = $('#' + options.controlId).closest('.fileupload-group');
3482                    $el.find('.js-upload-progress').rockFadeIn();
3483                },
3484                progressall: function (e, data) {
3485                    try {
3486                        if (data.total > 0) {
3487                            var $el = $('#' + options.controlId).closest('.fileupload-group');
3488                            var $progressPercent = $el.find('.js-upload-progress-percent');
3489                            if (!$progressPercent.length) {
3490                                return;
3491                            }
3492
3493                            var percent = (data.loaded * 100 / data.total).toFixed(0);
3494                            if (percent > 1 && percent < 99) {
3495                                $progressPercent.text(percent + "%"); 
3496                            }
3497                            else {
3498                                $progressPercent.text("");
3499                            }
3500                        }
3501                    }
3502                    catch (ex) {
3503                        // ignore if any exception occurs
3504                    }
3505                },
3506                stop: function (e) {
3507                    var $el = $('#' + options.controlId).closest('.fileupload-group');
3508                    $el.find('.js-upload-progress').hide();
3509                    $el.find('.fileupload-dropzone').show();
3510                },
3511                done: function (e, data) {
3512                    var $el = $('#' + options.aFileName);
3513
3514                    if ((options.isBinaryFile || 'T') === 'F') {
3515                        $('#' + options.hfFileId).val(data.response().result.FileName);
3516                    }
3517                    else {
3518                        $('#' + options.hfFileId).val(data.response().result.Id);
3519                    }
3520
3521                    var getFileUrl = Rock.settings.get('baseUrl')
3522                        + 'GetFile.ashx?'
3523                        + 'isBinaryFile=' + (options.isBinaryFile || 'T')
3524                        // note rootFolder is encrypted to prevent direct access to filesystem via the URL
3525                        + '&rootFolder=' + (encodeURIComponent(options.rootFolder) || '');
3526
3527                    if (options.disablePredictableIds) {
3528                        getFileUrl += '&guid=' + data.response().result.Guid;
3529                    }
3530                    else {
3531                        getFileUrl += '&id=' + data.response().result.Id
3532                    }
3533
3534                    getFileUrl += '&fileName=' + data.response().result.FileName;
3535
3536                    $el.text(data.response().result.FileName).attr('href', getFileUrl);
3537                    $('#' + options.aRemove).show();
3538                    if (options.postbackScript) {
3539                        window.location = "javascript:" +  options.postbackScript;
3540                    }
3541
3542                    if (options.doneFunction) {
3543                        options.doneFunction(e, data);
3544                    }
3545                },
3546                fail: function (e, data) {
3547                    var $el = $('#' + options.controlId).closest('.fileupload-group');
3548                    $el.siblings('.js-rockupload-alert').remove();
3549                    var $warning = $('<div class="alert alert-warning alert-dismissable js-rockupload-alert"/>');
3550
3551                    var msg = "unable to upload";
3552                    if (data.response().jqXHR && data.response().jqXHR.status == 406) {
3553                        msg = "file type not allowed";
3554                    } else if (data.response().jqXHR && data.response().jqXHR.responseText) {
3555                        msg = data.response().jqXHR.responseText;
3556                    }
3557
3558                    if (options.maxUploadBytes && data.total) {
3559                        if (data.total >= options.maxUploadBytes) {
3560                            msg = "file size is limited to " + (options.maxUploadBytes / 1024 / 1024) + "MB";
3561                        }
3562                    }
3563
3564                    $warning.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>')
3565                        .append('<strong><i class="ti ti-alert-triangle"></i> Warning </strong>')
3566                        .append(msg);
3567                    $warning.insertBefore($el);
3568                }
3569            });
3570
3571            $('#' + options.aRemove).on('click', function () {
3572                $(this).hide();
3573                var $el = $('#' + options.aFileName);
3574                $el.attr('href', '#');
3575                $el.text('');
3576                $el.removeClass('file-exists');
3577
3578                if (options.postbackRemovedScript) {
3579                    window.location = "javascript:" + options.postbackRemovedScript;
3580                } else {
3581                    $('#' + options.hfFileId).val('0');
3582                }
3583
3584                return false;
3585            });
3586        },
3587        exports = {
3588            initialize: function (options) {
3589                if (!options.controlId) throw 'Control ID must be set.';
3590                _configure(options);
3591            }
3592        };
3593
3594        return exports;
3595    }());
3596}(jQuery));
3597;
3598(function ($) {
3599    'use strict';
3600    window.Rock = window.Rock || {};
3601    Rock.controls = Rock.controls || {};
3602
3603    Rock.controls.firstNameTextBox = (function () {
3604        const exports = {
3605            initialize: function (options) {
3606                if (!options.id) {
3607                    throw new Error('id is required');
3608                }
3609                this.options = options;
3610            },
3611            clientValidate: function (validator, args) {
3612                const firstNameTextBox = document.getElementById(this.options.id);
3613                const notAllowedStrings = this.options.notAllowedStrings;
3614                let firstName = undefined;
3615                if (firstNameTextBox) {
3616                    firstName = firstNameTextBox.value.toUpperCase();
3617                }
3618
3619                let isValid = true;
3620                let invalidString;
3621
3622                if (firstName) {
3623                    for (const value of notAllowedStrings) {
3624                        if (firstName.includes(value.toUpperCase())) {
3625                            isValid = false;
3626                            invalidString = value;
3627                            break;
3628                        }
3629                    }
3630                }
3631                if (!isValid) {
3632                    const labelText = firstNameTextBox.dataset.itemLabel;
3633                    validator.errormessage = `${labelText} cannot contain: ${invalidString}`;
3634
3635                    if (this.options.displayInlineValidationError) {
3636                        validator.innerHTML = validator.errormessage;
3637                    }
3638                }
3639
3640                args.IsValid = isValid;
3641            }
3642        };
3643
3644        return exports;
3645    }());
3646}(jQuery));
3647;
3648(function ($) {
3649    'use strict';
3650    window.Rock = window.Rock || {};
3651    Rock.controls = Rock.controls || {};
3652
3653    Rock.controls.followingsToggler = (function () {
3654
3655        var exports = {
3656            // initialize a script that will set the following status via REST
3657            // entityTypeId is the EntityType, and entityId is the .Id for the associated entity
3658            // personId and personAliasId are the person that is doing the following/un-following
3659            // callback is optional and called on follow or unfollow events with a param indicating
3660            // if the person is now following
3661            initialize: function ($followingDiv, entityTypeId, entityId, purposeKey, personId, personAliasId, callback) {
3662                var hasCallback = typeof callback === 'function';
3663
3664                $followingDiv.on('click', function () {
3665                    if ($followingDiv.hasClass('following')) {
3666
3667                        $.ajax({
3668                            type: 'DELETE',
3669                            url: Rock.settings.get('baseUrl') + 'api/followings/' + entityTypeId + '/' + entityId + '/' + personId + '?purposeKey=' + encodeURIComponent(purposeKey),
3670                            success: function (data, status, xhr) {
3671                                $followingDiv.removeClass('following');
3672
3673                                // update the tooltip (if one was configured)
3674                                if ($followingDiv.attr('data-original-title')) {
3675                                    $followingDiv.attr('data-original-title', 'Click to follow');
3676                                }
3677
3678                                if (hasCallback) {
3679                                    callback(false, $followingDiv, entityTypeId, entityId, personId, personAliasId);
3680                                }
3681                            },
3682                        });
3683
3684                    } else {
3685                        var following = {
3686                            EntityTypeId: entityTypeId,
3687                            EntityId: entityId,
3688                            PersonAliasId: personAliasId,
3689                            PurposeKey: purposeKey
3690                        };
3691
3692                        $.ajax({
3693                            type: 'POST',
3694                            contentType: 'application/json',
3695                            data: JSON.stringify(following),
3696                            url: Rock.settings.get('baseUrl') + 'api/followings',
3697                            statusCode: {
3698                                201: function () {
3699                                    $followingDiv.addClass('following');
3700
3701                                    // update the tooltip (if one was configured)
3702                                    if ($followingDiv.attr('data-original-title')) {
3703                                        $followingDiv.attr('data-original-title', 'Currently following');
3704                                    }
3705
3706                                    if (hasCallback) {
3707                                        callback(true, $followingDiv, entityTypeId, entityId, personId, personAliasId);
3708                                    }
3709                                }
3710                            }
3711                        });
3712                    }
3713                })
3714            }
3715        };
3716
3717        return exports;
3718    }());
3719}(jQuery));
3720;
3721(function ($) {
3722    'use strict';
3723    window.Rock = window.Rock || {};
3724    Rock.controls = Rock.controls || {};
3725
3726    Rock.controls.fullScreen = (function () {
3727        var exports = {
3728            initialize: function (el) {
3729                $('.js-fullscreen-trigger').on('click', function (e) {
3730                    e.preventDefault();
3731                    var elem = $(this).closest('.block-instance')[0] || document.documentElement
3732                    if (el) {
3733                        // If Requesting Fullscreen page, verify layout is fullscreen capable.
3734                        if (el === 'body' && $(this).parents('.page-fullscreen-capable').length < 1) {
3735                            elem = $(this).closest('.block-instance')[0]
3736                        } else {
3737                            elem = $(el)[0];
3738                        }
3739                    }
3740                    Rock.controls.fullScreen.toggleFullscreen(elem);
3741                });
3742            },
3743            toggleFullscreen: function (elem) {
3744                elem = elem || document.documentElement;
3745                if (!document.fullscreenElement && !document.mozFullScreenElement &&
3746                    !document.webkitFullscreenElement && !document.msFullscreenElement) {
3747                    if (elem.requestFullscreen) {
3748                    elem.requestFullscreen();
3749                    document.addEventListener('fullscreenchange', this.exitHandler, false);
3750                    } else if (elem.msRequestFullscreen) {
3751                    elem.msRequestFullscreen();
3752                    document.addEventListener('MSFullscreenChange', this.exitHandler, false);
3753                    } else if (elem.mozRequestFullScreen) {
3754                    elem.mozRequestFullScreen();
3755                    document.addEventListener('mozfullscreenchange', this.exitHandler, false);
3756                    } else if (elem.webkitRequestFullscreen) {
3757                    elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
3758                    document.addEventListener('webkitfullscreenchange', this.exitHandler, false);
3759                    }
3760                    $(elem).addClass('is-fullscreen');
3761                } else {
3762                    if (document.exitFullscreen) {
3763                    document.exitFullscreen();
3764                    } else if (document.msExitFullscreen) {
3765                    document.msExitFullscreen();
3766                    } else if (document.mozCancelFullScreen) {
3767                    document.mozCancelFullScreen();
3768                    } else if (document.webkitExitFullscreen) {
3769                    document.webkitExitFullscreen();
3770                    }
3771                    $(elem).removeClass('is-fullscreen');
3772                }
3773            },
3774            exitHandler: function (elem) {
3775                if (!document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement) {
3776                    $(elem.target).removeClass('is-fullscreen');
3777                    document.dispatchEvent(new Event('RockExitFullscreen'));
3778                }
3779            }
3780        };
3781
3782        return exports;
3783    }());
3784}(jQuery));
3785;
3786(function ($) {
3787    'use strict';
3788    window.Rock = window.Rock || {};
3789    Rock.controls = Rock.controls || {};
3790
3791    Rock.controls.geoPicker = (function () {
3792
3793        var GeoPicker = function (options) {
3794            var obj = this;
3795            obj.controlId = options.controlId;
3796            obj.restUrl = options.restUrl;
3797            obj.path = options.path;
3798            obj.centerAddress = options.centerAddress;                      // used when nothing is on map
3799            obj.centerLatitude = options.centerLatitude || "33.590795";     // used when nothing is on map
3800            obj.centerLongitude = options.centerLongitude || "-112.126459"; // used when nothing is on map
3801            obj.drawingMode = options.drawingMode || "Polygon" || "Point";  // the available modes
3802            obj.strokeColor = options.strokeColor || "#0088cc";
3803            obj.fillColor = options.fillColor || "#0088cc";
3804            obj.mapId = options.mapId;
3805
3806            // An array of styles that controls the look of the Google Map
3807            // http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html
3808            obj.styles = options.mapStyle;
3809
3810            // This is used to temporarily store in case of user cancel.
3811            obj.pathTemp = null;
3812
3813            // the selected polygon or point
3814            obj.selectedShape = null;
3815
3816            // these are used to set the map's viewport boundary
3817            obj.minLat = null;
3818            obj.maxLat = null;
3819            obj.minLng = null;
3820            obj.maxLng = null;
3821
3822            // our google Map
3823            obj.map = null;
3824
3825            // an instance of the Google Maps drawing manager
3826            obj.drawingManager = null;
3827
3828            // An array of styles that controls the look of the Google Map
3829            // http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html
3830            //obj.styles = [
3831            //  {
3832            //      "stylers": [
3833            //        { "visibility": "simplified" },
3834            //        { "saturation": -100 }
3835            //      ]
3836            //  }, {
3837            //      "featureType": "road",
3838            //      "elementType": "labels",
3839            //      "stylers": [
3840            //        { "visibility": "on" }
3841            //      ]
3842            //  }, {
3843            //      "featureType": "poi",
3844            //      "stylers": [
3845            //        { "visibility": "off" }
3846            //      ]
3847            //  }, {
3848            //      "featureType": "landscape",
3849            //      "stylers": [
3850            //        { "visibility": "off" }
3851            //      ]
3852            //  }, {
3853            //      "featureType": "administrative.province",
3854            //      "stylers": [
3855            //        { "visibility": "on" }
3856            //      ]
3857            //  }, {
3858            //      "featureType": "administrative.locality",
3859            //      "stylers": [
3860            //        { "visibility": "on" }
3861            //      ]
3862            //  }
3863            //];
3864
3865            /**
3866            * Initializes the map viewport boundary coordinates.
3867            */
3868            this.initMinMaxLatLng = function () {
3869                obj.minLat = null;
3870                obj.maxLat = null;
3871                obj.minLng = null;
3872                obj.maxLng = null;
3873            }
3874
3875            /**
3876            * Unselects the selected shape (if selected) and disables the delete button.
3877            */
3878            this.clearSelection = function () {
3879                if (obj.selectedShape) {
3880                    if (obj.drawingMode == "Polygon") {
3881                        obj.selectedShape.setEditable(false);
3882                    }
3883                    obj.selectedShape = null;
3884                }
3885                $('#gmnoprint-delete-button_' + obj.controlId).attr('disabled', '')
3886                    .find('.ti-x').css("color", "#aaa");
3887            }
3888
3889            /**
3890            * Stores the given shape's path points into the path property as a
3891            * pipe (|) delimited list of lat,long coordinates.  This method also
3892            * re-enables the delete button so that the polygon can be deleted.
3893            */
3894            this.setSelection = function (shape, type) {
3895                obj.clearSelection();
3896
3897                // enable delete button
3898                $('#gmnoprint-delete-button_' + obj.controlId).prop("disabled", false)
3899                    .find('.ti-x').css("color", "");
3900
3901                obj.selectedShape = shape;
3902
3903                if (type == "polygon") {
3904                    shape.setEditable(true);
3905                    var coordinates = new Array();
3906                    var vertices = shape.getPaths().getAt(0);
3907                    // Iterate over the vertices of the shape's path
3908                    for (var i = 0; i < vertices.length; i++) {
3909                        var xy = vertices.getAt(i);
3910                        coordinates[i] = xy.toUrlValue();
3911                    }
3912
3913                    // if the last vertex is not already the first, then
3914                    // add the first vertex to the end of the path.
3915                    if (vertices.getAt(0).toUrlValue() != coordinates[coordinates.length - 1]) {
3916                        obj.path = coordinates.join('|') + '|' + vertices.getAt(0).toUrlValue();
3917                    }
3918                    else {
3919                        obj.path = coordinates.join('|');
3920                    }
3921                }
3922                else if (type == "marker") {
3923                    obj.path = shape.getPosition().toUrlValue();
3924                }
3925            }
3926
3927            /**
3928            * Delete the selected shape and enable the drawing controls
3929            * if they were deleted.  Also removes the polygon from the hidden variable.
3930            */
3931            this.deleteSelectedShape = function () {
3932                if (obj.selectedShape && confirm("Delete selected shape?")) {
3933                    obj.selectedShape.setMap(null);
3934                    obj.clearSelection();
3935
3936                    // delete the path
3937                    obj.path = null;
3938
3939                    // enable the drawing controls again
3940                    obj.drawingManager.setOptions({
3941                        drawingControlOptions: {
3942                            drawingModes: obj.getDrawingModes()
3943                        }
3944                    });
3945                }
3946            }
3947
3948            /**
3949            * Returns the appropriate mode array for use with the map's DrawingManager
3950            * drawing control options.
3951            */
3952            this.getDrawingModes = function getDrawingModes() {
3953
3954                if (obj.drawingMode == "Polygon") {
3955                    return [google.maps.drawing.OverlayType.POLYGON];
3956                }
3957                else if (obj.drawingMode == "Point") {
3958                    return [google.maps.drawing.OverlayType.MARKER];
3959                }
3960            }
3961
3962            /**
3963            * Returns a marker image styled according to the stroke color.
3964            */
3965            this.getMarkerImage = function getMarkerImage() {
3966                return {
3967                    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',
3968                    fillColor: '#FE7569',
3969                    fillOpacity: 1,
3970                    strokeColor: '#000',
3971                    strokeWeight: 1,
3972                    scale: 1,
3973                    labelOrigin: new google.maps.Point(0, -28)
3974                }
3975            }
3976
3977            /**
3978            * Finds the point/polygon boundary and sets the map viewport to fit
3979            */
3980            this.fitBounds = function () {
3981                if (! obj.path) {
3982                    // if no path, then set the center using the options
3983                    var newLatLng = new google.maps.LatLng(
3984                        parseFloat(obj.centerLatitude),
3985                        parseFloat(obj.centerLongitude));
3986                    obj.map.setCenter(newLatLng);
3987                    return;
3988                }
3989
3990                var coords = obj.path.split('|');
3991                var pathArray = new Array();
3992                // find the most southWest and northEast points of the path.
3993                for (var i = 0; i < coords.length ; i++) {
3994                    var latLng = coords[i].split(',');
3995                    var lat = parseFloat(latLng[0]);
3996                    var lng = parseFloat(latLng[1]);
3997                    // find the most southWest and northEast points of the path.
3998                    obj.findBounds(lat, lng);
3999                    pathArray.push(new google.maps.LatLng(lat, lng));
4000                }
4001
4002                // Set the viewport to contain the given bounds.
4003                var southWest = new google.maps.LatLng(obj.minLat, obj.minLng);
4004                var northEast = new google.maps.LatLng(obj.maxLat, obj.maxLng);
4005                var bounds = new google.maps.LatLngBounds(southWest, northEast);
4006                obj.map.fitBounds(bounds);
4007
4008                if (obj.drawingMode == "Point") {
4009                    obj.map.setZoom(16);
4010                }
4011            }
4012
4013            /**
4014            * Utility method to determine the most SouthWestern
4015            * and NorthEastern lat and long.
4016            */
4017            this.findBounds = function (lat, lng) {
4018                if (!obj.minLat || lat < obj.minLat) {
4019                    obj.minLat = lat;
4020                }
4021                if (!obj.maxLat || lat > obj.maxLat) {
4022                    obj.maxLat = lat;
4023                }
4024                if (!obj.minLng || lng < obj.minLng) {
4025                    obj.minLng = lng;
4026                }
4027                if (!obj.maxLng || lng > obj.maxLng) {
4028                    obj.maxLng = lng;
4029                }
4030            }
4031
4032            /**
4033            * Disables the drawing manager so they cannot add anything to the map.
4034            */
4035            this.disableDrawingManager = function () {
4036                // Switch back to non-drawing mode after drawing a shape.
4037                if (!obj.drawingManager) {
4038                    return;
4039                }
4040
4041                obj.drawingManager.setDrawingMode(null);
4042
4043                // disable the drawing controls so we only get one polygon
4044                // and we'll add it back on deleting the existing polygon.
4045                obj.drawingManager.setOptions({
4046                    drawingControlOptions: {
4047                        drawingModes: [
4048                        ]
4049                    }
4050                });
4051            }
4052
4053            /**
4054            * Takes the path data stored in the path and plots it on the map.
4055            */
4056            this.plotPath = function (map) {
4057                obj.initMinMaxLatLng();
4058                // only try this if we have a path
4059                if (obj.path) {
4060                    var coords = obj.path.split('|');
4061                    var pathArray = new Array();
4062                    // put the polygon coordinates into a path array
4063                    for (var i = 0; i < coords.length ; i++) {
4064                        var latLng = coords[i].split(',');
4065                        var lat = parseFloat(latLng[0]);
4066                        var lng = parseFloat(latLng[1]);
4067                        pathArray.push(new google.maps.LatLng(lat, lng));
4068                    }
4069
4070                    // reverse geocode the first point to an address and display.
4071                    var $label = $('#selectedGeographyLabel_' + this.controlId);
4072                    obj.toAddress(pathArray[0], $label);
4073
4074                    if (coords.length > 0) {
4075                        var polygon;
4076
4077                        if (obj.drawingMode == "Polygon") {
4078
4079                            var polygon = new google.maps.Polygon({
4080                                path: pathArray,
4081                                clickable: true,
4082                                editable: true,
4083                                strokeColor: obj.strokeColor,
4084                                fillColor: obj.fillColor,
4085                                strokeWeight: 2
4086                            });
4087                            polygon.setMap(map);
4088
4089                            // Select the polygon
4090                            obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
4091
4092                            // Disable the drawing manager
4093                            obj.disableDrawingManager();
4094
4095                            // add listener for moving polygon points.
4096                            google.maps.event.addListener(polygon.getPath(), 'set_at', function (e) {
4097                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
4098                            });
4099
4100                            // add listener for adding new points.
4101                            google.maps.event.addListener(polygon.getPath(), 'insert_at', function (e) {
4102                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
4103                            });
4104
4105                            // Add an event listener to implement right-click to delete node
4106                            google.maps.event.addListener(polygon, 'rightclick', function (ev) {
4107                                if (ev.vertex != null) {
4108                                    polygon.getPath().removeAt(ev.vertex);
4109                                }
4110                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
4111                            });
4112
4113                            // add listener for "selecting" the polygon
4114                            // because that's where we stuff the coordinates into the hidden variable
4115                            google.maps.event.addListener(polygon, 'click', function () {
4116                                obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON);
4117                            });
4118                        }
4119                        else if (obj.drawingMode == "Point") {
4120                            var point;
4121
4122                            if (!obj.mapId) {
4123                                point = new google.maps.Marker({
4124                                    position: pathArray[0],
4125                                    map: map,
4126                                    clickable: true,
4127                                    icon: obj.getMarkerImage()
4128                                });
4129                            }
4130                            else {
4131                                var pin = new google.maps.marker.PinElement({
4132                                    background: '#FE7569',
4133                                    borderColor: '#000',
4134                                    scale: 1,
4135                                    glyph: ''
4136                                });
4137
4138                                point = {
4139                                    position: pathArray[0],
4140                                    map: map,
4141                                    clickable: true,
4142                                    icon: obj.getMarkerImage(),
4143                                    marker_element: new google.maps.marker.AdvancedMarkerElement({
4144                                        position: pathArray[0],
4145                                        map: map,
4146                                        content: pin.element
4147                                    }),
4148                                    setMap: function (newMap) {
4149                                        this.map = newMap;
4150                                        this.marker_element.map = newMap;
4151                                    },
4152                                    getPosition: function () {
4153                                        return this.position;
4154                                    },
4155                                    setPosition: function (newPosition) {
4156                                        this.position = newPosition;
4157                                        this.marker_element.position = newPosition;
4158                                    }
4159                                };
4160                            }
4161
4162                            // Select the point
4163                            obj.setSelection(point, google.maps.drawing.OverlayType.MARKER);
4164
4165                            // Disable the drawing manager
4166                            obj.disableDrawingManager();
4167
4168                            // add listener for "selecting" the point
4169                            // because that's where we stuff the coordinates into the hidden variable
4170                            google.maps.event.addListener(point, 'click', function () {
4171                                obj.setSelection(point, google.maps.drawing.OverlayType.MARKER);
4172                            });
4173                        }
4174                    }
4175                }
4176            }
4177
4178            /**
4179            * Take the given lat and long and try to reverse-geocode to an Address
4180            * then stuff that address as the value of the given element.
4181            */
4182            this.toAddress = function (latlng, $labelElement) {
4183                // only try if we have a valid latlng
4184                if (!latlng || isNaN(latlng.lat()) || isNaN(latlng.lng())) {
4185                    $labelElement.text('');
4186                    return;
4187                }
4188                var geocoder = new google.maps.Geocoder();
4189                geocoder = geocoder.geocode({ "latLng": latlng }, function (results, status) {
4190                    if (status == google.maps.GeocoderStatus.OK) {
4191                        if (results[0]) {
4192                            $labelElement.text('near ' + results[0].formatted_address);
4193                        }
4194                    }
4195                    else
4196                    {
4197                        $labelElement.html('<i>selected</i>');
4198                        console.log('Geocoder failed due to: ' + status);
4199                    }
4200                });
4201            }
4202
4203            /**
4204            * Geocode an address to a latLng and center the map on that point.
4205            */
4206            this.centerMapOnAddress = function () {
4207                var self = this;
4208                // only try if a centerAddress is set
4209                if (!obj.centerAddress) {
4210                    return;
4211                }
4212
4213                var geocoder = new google.maps.Geocoder();
4214
4215                geocoder.geocode({ 'address': obj.centerAddress }, function (results, status) {
4216                    if (status == google.maps.GeocoderStatus.OK) {
4217                        self.map.setCenter(results[0].geometry.location);
4218                    } else {
4219                        console.log('Geocode was not successful for the following reason: ' + status);
4220                    }
4221                });
4222            }
4223
4224            /**
4225            * Return a latLng for the "first point" of the selected map.
4226            */
4227            this.firstPoint = function () {
4228                if (obj.path) {
4229                    var coords = obj.path.split('|');
4230                    if (coords) {
4231                        var latLng = coords[0].split(',');
4232                        var lat = parseFloat(latLng[0]);
4233                        var lng = parseFloat(latLng[1]);
4234                        return new google.maps.LatLng(lat, lng);
4235                    }
4236                }
4237            }
4238
4239        }; // sorta end class
4240
4241        /**
4242        * initialize our event handlers for the button clicks we're going to be handling.
4243        */
4244        GeoPicker.prototype.initializeEventHandlers = function () {
4245            var controlId = this.controlId,
4246                $control = $('#' + this.controlId),
4247                $hiddenField = $('#' + this.controlId + '_hfGeoPath'),
4248                self = this;
4249
4250            /**
4251            * Toggle the picker on and off when the control's link is clicked.
4252            */
4253            $('#' + controlId + ' .picker-label').on('click', function (e) {
4254                e.preventDefault();
4255                var $control = $('#' + controlId);
4256                $(this).toggleClass("active");
4257                $control.find('.picker-menu').first().toggle(0, function () {
4258                    Rock.dialogs.updateModalScrollBar(controlId);
4259                });
4260
4261                if ( $control.find('.picker-menu').first().is(":visible") ) {
4262                    google.maps.event.trigger(self.map, "resize");
4263                    // now we can safely fit the map to any polygon boun
4263dary
4264                    self.fitBounds();
4265
4266                    // Scroll down so you can see the done/cancel buttons
4267                    $("html,body").animate({
4268                        scrollTop: $control.offset().top
4269                    }, 1000);
4270                }
4271            });
4272
4273            /**
4274            * Hide the expand button if we are in a modal
4275            */
4276            if ($control.closest('.modal').length) {
4277                $('#btnExpandToggle_' + controlId).hide();
4278            }
4279
4280            /**
4281            * Handle the toggle expand fullscreen button click.
4282            */
4283            $('#btnExpandToggle_' + controlId).on('click', function () {
4284
4285                var $myElement = $('#geoPicker_' + self.controlId);
4286
4287                var isExpaned = $myElement.data("fullscreen");
4288
4289                $(this).children('i').toggleClass("ti-maximize", isExpaned);
4290                $(this).children('i').toggleClass("ti-minimize", ! isExpaned);
4291
4292                // Shrink to regular size
4293                if ( isExpaned ) {
4294                    $myElement.data("fullscreen", false);
4295
4296                    $(this).closest('.picker-menu').css({
4297                        position: 'absolute',
4298                        top: 0,
4299                        left: 0,
4300                        height: '',
4301                        width: 520
4302                    });
4303                    // resize the map
4304                    $myElement.css({
4305                        height: 300,
4306                        width: 500
4307                    });
4308
4309                    // move the delete button
4310                    $('#gmnoprint-delete-button_' + self.controlId).css({
4311                        left: '200px',
4312                    });
4313
4314                }
4315                else {
4316
4317                    // Expand to fullscreen
4318
4319                    $myElement.data("fullscreen", true);
4320                    // resize the container
4321                    $(this).closest('.picker-menu').css({
4322                        position: 'fixed',
4323                        top: 0,
4324                        left: 0,
4325                        height: '100%',
4326                        width: '100%'
4327                    });
4328
4329                    // resize the map
4330                    $myElement.css({
4331                        height: '85%',
4332                        width: '100%'
4333                    });
4334
4335                    // move the delete button
4336                    $('#gmnoprint-delete-button_' + self.controlId).css({
4337                        left: '200px',
4338                    });
4339                }
4340
4341                // tell the map to resize/redraw
4342                google.maps.event.trigger(self.map, 'resize');
4343                self.fitBounds();
4344
4345                Rock.dialogs.updateModalScrollBar(self.controlId);
4346
4347            });
4348
4349            /**
4350            * Handle the Cancel button click by hiding the overlay.
4351            */
4352            $('#btnCancel_' + controlId).on('click', function () {
4353                $(this).closest('.picker-menu').slideUp(function () {
4354                    Rock.dialogs.updateModalScrollBar(controlId);
4355                });
4356
4357                self.path = self.pathTemp;
4358
4359                if ( self.selectedShape ) {
4360                    self.selectedShape.setMap(null);
4361                    self.clearSelection();
4362
4363                    // enable the drawing controls again
4364                    self.drawingManager.setOptions({
4365                        drawingControlOptions: {
4366                            drawingModes: self.getDrawingModes()
4367                        }
4368                    });
4369                }
4370                self.plotPath(self.map);
4371            });
4372
4373            // have the X appear on hover if something is selected
4374            if ($hiddenField.val() && $hiddenField.val() !== '0') {
4375                $control.find('.picker-select-none').addClass('rollover-item').show();
4376            }
4377
4378            /**
4379            * Handle the Select button click by stuffing the RockGoogleGeoPicker's path value into the hidden field.
4380            */
4381            $control.find('.js-geopicker-select').on('click', function () {
4382                var geoInput = $('#' + controlId).find('input:checked'),
4383                    selectedValue = self.path,
4384                    selectedGeographyLabel = $('#selectedGeographyLabel_' + controlId);
4385
4386                $hiddenField.val(self.path);
4387
4388                // have the X appear on hover. something is selected
4389                $control.find('.picker-select-none').addClass('rollover-item');
4390                $control.find('.picker-select-none').show();
4391
4392                selectedGeographyLabel.val(selectedValue);
4393                self.toAddress( self.firstPoint(), selectedGeographyLabel);
4394
4395                //clear out any old map positioning
4396                self.initMinMaxLatLng();
4397
4398                $(this).closest('.picker-menu').slideUp(function () {
4399                    Rock.dialogs.updateModalScrollBar(controlId);
4400                });
4401
4402                $(this).closest('form').submit();
4403            });
4404
4405
4406            /**
4407            * Clear the selection when X is clicked
4408            */
4409            $control.find('.picker-select-none').on('click', function (e) {
4410                e.stopImmediatePropagation();
4411                var selectedGeographyLabel = $('#selectedGeographyLabel_' + controlId);
4412                $hiddenField.val("");
4413
4414                // don't have the X appear on hover. nothing is selected
4415                $control.find('.picker-select-none').removeClass('rollover-item');
4416                $control.find('.picker-select-none').hide();
4417
4418                selectedGeographyLabel.val("");
4419                self.toAddress(null, selectedGeographyLabel);
4420
4421                //clear out any old map positioning
4422                self.initMinMaxLatLng();
4423
4424                return false;
4425            });
4426        };
4427
4428        /**
4429        * Initialize the GeoPicker.
4430        */
4431        GeoPicker.prototype.initialize = function () {
4432            var self = this;
4433            var $hiddenField = $('#' + self.controlId + '_hfGeoPath');
4434            var deleteButtonId = 'gmnoprint-delete-button_' + self.controlId;
4435
4436            // Pull anything in the hidden field onto this object's path
4437            self.path = $hiddenField.val();
4438
4439            // store path into pathTemp in case of user cancel.
4440            self.pathTemp = self.path;
4441
4442            // Create a new StyledMapType object, passing it the array of styles,
4443            // as well as the name to be displayed on the map type control.
4444            var styledMap = new google.maps.StyledMapType(self.styles, { name: "Styled Map" });
4445
4446            // WARNING: I though about removing the "center:" from the options here but then the
4447            // map's controls were different and then our delete button was out of alignment.
4448            var mapOptions = {
4449                center: new google.maps.LatLng(
4450                        parseFloat(self.centerLatitude),
4451                        parseFloat(self.centerLongitude)),
4452                zoom: 13,
4453                streetViewControl: false,
4454                mapTypeControlOptions: {
4455                    mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'map_style']
4456                },
4457            };
4458
4459            if (self.mapId) {
4460                mapOptions.mapId = self.mapId;
4461            }
4462
4463            // center the map on the configured address
4464            self.centerMapOnAddress();
4465
4466            self.map = new google.maps.Map(document.getElementById('geoPicker_' + self.controlId), mapOptions);
4467
4468            //Associate the styled map with the MapTypeId and set it to display.
4469            self.map.mapTypes.set('map_style', styledMap);
4470            self.map.setMapTypeId('map_style');
4471
4472            // If we have coordinates we should plot them here...
4473            self.plotPath(self.map);
4474
4475            // Set up the Drawing Manager for creating polygons, circles, etc.
4476            self.drawingManager = new google.maps.drawing.DrawingManager({
4477                drawingControl: true,
4478                drawingControlOptions: {
4479                    drawingModes: self.getDrawingModes()
4480                },
4481                polygonOptions: {
4482                    editable: true,
4483                    strokeColor: self.strokeColor,
4484                    fillColor: self.fillColor,
4485                    strokeWeight: 2
4486                },
4487                markerOptions: {
4488                    icon: self.getMarkerImage()
4489                }
4490            });
4491
4492            self.drawingManager.setMap(self.map);
4493
4494            // but disable the drawing manager if we already have a point/polygon selected:
4495            if (self.path) {
4496                self.disableDrawingManager();
4497            }
4498
4499            // Handle when the polygon shape drawing is "complete"
4500            google.maps.event.addListener(self.drawingManager, 'overlaycomplete', function (e) {
4501                if (e.type == google.maps.drawing.OverlayType.POLYGON || e.type == google.maps.drawing.OverlayType.MARKER ) {
4502                    // Disable the drawing manager once they've drawn an overlay.
4503                    self.disableDrawingManager();
4504
4505                    // Add an event listener that selects the newly-drawn shape when the user mouses down on it.
4506                    var newShape = e.overlay;
4507                    newShape.type = e.type;
4508                    google.maps.event.addListener(newShape, 'click', function () {
4509                        self.setSelection(newShape, e.type);
4510                    });
4511                    self.setSelection(newShape, e.type);
4512
4513                    if (e.type == google.maps.drawing.OverlayType.POLYGON) {
4514                        // add listener for moving polygon points.
4515                        google.maps.event.addListener(newShape.getPath(), 'set_at', function (e) {
4516                            self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON);
4517                        });
4518
4519                        // add listener for adding new points.
4520                        google.maps.event.addListener(newShape.getPath(), 'insert_at', function (e) {
4521                            self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON);
4522                        });
4523
4524                        // Add an event listener to implement right-click to delete node
4525                        google.maps.event.addListener(newShape, 'rightclick', function (ev) {
4526                            if (ev.vertex != null) {
4527                                newShape.getPath().removeAt(ev.vertex);
4528                            }
4529                            self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON);
4530                        });
4531                    }
4532                }
4533            });
4534
4535            // Clear the current selection when the drawing mode is changed, or when the map is clicked.
4536            google.maps.event.addListener(self.drawingManager, 'drawingmode_changed', self.clearSelection);
4537            google.maps.event.addListener(self.map, 'click', self.clearSelection);
4538
4539            // Move our custom delete button into place once the map is idle.
4540            // as per http://stackoverflow.com/questions/832692/how-to-check-if-google-maps-is-fully-loaded
4541            google.maps.event.addListenerOnce(self.map, 'idle', function () {
4542                $('#' + deleteButtonId).fadeIn();
4543
4544                // wire up an event handler to the delete button
4545                document.getElementById(deleteButtonId).addEventListener('click', self.deleteSelectedShape);
4546            });
4547
4548            self.initializeEventHandlers();
4549
4550            Rock.dialogs.updateModalScrollBar(self.controlId);
4551        };
4552
4553        var exports = {
4554            googleMapsLoadCallback: function () {
4555                // callback for when google maps api is done loading (if it wasn't loaded already)
4556                $.each(Rock.controls.geoPicker.geoPickerOptions, function (a, options) {
4557                    var geoPicker = Rock.controls.geoPicker.geoPickers[options.controlId];
4558                    if (!geoPicker) {
4559
4560                        geoPicker = new GeoPicker(options);
4561                        Rock.controls.geoPicker.geoPickers[options.controlId] = geoPicker;
4562                        geoPicker.initialize();
4563                    }
4564                });
4565            },
4566            geoPickers: {},
4567            geoPickerOptions: {},
4568            findControl: function (controlId) {
4569                return exports.geoPickers[controlId];
4570            },
4571            initialize: function (options) {
4572                if (!options.controlId) throw '`controlId` is required.';
4573                exports.geoPickerOptions[options.controlId] = options;
4574
4575                $(window).on('googleMapsIsLoaded', this.googleMapsLoadCallback);
4576
4577                // if the google maps api isn't loaded uet, googleMapsLoadCallback will take care of it
4578                if (typeof (google) != "undefined") {
4579                    // null it out just in case, to force it to get recreated
4580                    exports.geoPickers[options.controlId] = null;
4581                    $(window).trigger('googleMapsIsLoaded');
4582                }
4583            }
4584        };
4585
4586        return exports;
4587    }());
4588}(jQuery));
4589;
4590(function (Sys) {
4591    'use strict';
4592    Sys.Application.add_load(function () {
4593        $('a.help').on('click', function (e) {
4594            e.preventDefault();
4595            $(this).siblings('div.alert-info').slideToggle(function () {
4596                Rock.controls.modal.updateSize(this);
4597            });
4598            $(this).siblings('a.warning').insertAfter($(this));
4599            $(this).siblings('div.alert-warning').slideUp();
4600        });
4601    });
4602}(Sys));
4603;
4604(function ($) {
4605    'use strict';
4606    window.Rock = window.Rock || {};
4607
4608    Rock.htmlEditor = (function () {
4609        var exports = {
4610            // full list of available items is at http://summernote.org/deep-dive/#custom-toolbar-popover
4611
4612            // the toolbar items to include in the HtmlEditor when Toolbar is set to Light 
4613            toolbar_RockCustomConfigLight:
4614                [
4615                    // [groupName, [list of button]]
4616                    ['source_group', ['rockcodeeditor']],
4617                    ['style_group1', ['bold', 'italic', 'strikethrough', 'link', 'color', 'style', 'ol', 'ul']],
4618                    ['style_group3', ['clear']],
4619                    ['para', ['paragraph']],
4620                    ['plugins1', ['rockmergefield']],
4621                    ['plugins2', ['rockimagebrowser', 'rockfilebrowser', 'rockassetmanager']],
4622                    ['plugins3', ['rockpastetext', 'rockpastefromword']],
4623                    ['style_group2', ['undo', 'redo']]
4624                ],
4625
4626            // the toolbar items to include in the HtmlEditor when Toolbar is set to Full 
4627            toolbar_RockCustomConfigFull:
4628                [
4629                    // [groupName, [list of button]]
4630                    ['source_group', ['rockcodeeditor']],
4631                    ['style_group1', ['bold', 'italic', 'underline', 'strikethrough', 'ol', 'ul', 'link']],
4632                    ['style_group2', ['undo', 'redo']],
4633                    ['style_group3', ['clear']],
4634                    ['style_group4', ['style', 'color']],
4635                    ['full_toolbar_only', ['fontname', 'fontsize', 'superscript', 'subscript', 'table', 'hr']],
4636                    ['para', ['paragraph']],
4637                    ['plugins1', ['rockmergefield']],
4638                    ['plugins2', ['rockimagebrowser', 'rockfilebrowser', 'rockassetmanager']],
4639                    ['plugins3', ['rockpastetext', 'rockpastefromword']],
4640                    ['help_group1', ['help']]
4641                ]
4642        };
4643
4644        return exports;
4645
4646    }());
4647}(jQuery));
4648;
4649(function ($) {
4650    'use strict';
4651    window.Rock = window.Rock || {};
4652    Rock.controls = Rock.controls || {};
4653
4654    Rock.controls.imageUploader = (function () {
4655        var _configure = function (options) {
4656            options.isBinaryFile = options.isBinaryFile || 'T';
4657
4658            var $contentFileSource = $('#' + options.hfContentFileSource);
4659            var $binaryFileId = $('#' + options.hfFileId);
4660
4661            // default setImageUrlOnUpload to true if not specified
4662            if (options.setImageUrlOnUpload == null) {
4663                options.setImageUrlOnUpload = true;
4664            }
4665
4666            var wsUrl = Rock.settings.get('baseUrl')
4667                + 'ImageUploader.ashx?'
4668                + 'isBinaryFile=' + options.isBinaryFile;
4669
4670            if (options.isBinaryFile == 'T') {
4671                wsUrl += '&fileId=' + options.fileId
4672                    + '&fileTypeGuid=' + options.fileTypeGuid;
4673            }
4674            else {
4675                // note rootFolder is encrypted to prevent direct access to filesystem via the URL
4676                wsUrl += '&rootFolder=' + (encodeURIComponent(options.rootFolder) || '');
4677            }
4678
4679            if (options.isTemporary == 'F') {
4680                wsUrl += '&IsTemporary=False';
4681            }
4682
4683            // uses https://github.com/blueimp/jQuery-File-Upload
4684            $('#' + options.controlId).fileupload({
4685                url: wsUrl,
4686                dataType: 'json',
4687                dropZone: $('#' + options.controlId).closest('.imageupload-dropzone'),
4688                autoUpload: true,
4689                submit: options.submitFunction,
4690                start: function (e, data) {
4691                    var $el = $('#' + options.controlId).closest('.imageupload-group');
4692                    $el.find('.js-upload-progress').rockFadeIn();
4693                },
4694                progressall: function (e, data) {
4695                    // var $el = $('#' + options.controlId).closest('.imageupload-group');
4696                    // implement this to show progress percentage
4697                },
4698                stop: function (e) {
4699                    var $el = $('#' + options.controlId).closest('.imageupload-group');
4700                    $el.find('.js-upload-progress').hide();
4701                    $el.find('.imageupload-dropzone').rockFadeIn();
4702                },
4703                done: function (e, data) {
4704                    var $el = $('#' + options.imgThumbnail);
4705
4706                    if ((options.isBinaryFile || 'T') == 'F') {
4707                        $contentFileSource.val(data.response().result.FileName);
4708                    }
4709                    else {
4710                        $binaryFileId.val(data.response().result.Id);
4711                    }
4712
4713                    var getImageUrl = Rock.settings.get('baseUrl')
4714                        + 'GetImage.ashx?'
4715                        + 'isBinaryFile=' + (options.isBinaryFile || 'T');
4716
4717                    if (options.disablePredictableIds) {
4718                        getImageUrl += '&guid=' + data.response().result.Guid
4719                    }
4720                    else {
4721                        getImageUrl += '&id=' + data.response().result.Id;
4722                    }
4723
4724                    getImageUrl += '&fileName=' + data.response().result.FileName
4725                        + '&width=500';
4726
4727                    if (options.rootFolder) {
4728                        // note rootFolder is encrypted to prevent direct access to filesystem via the URL
4729                        getImageUrl += '&rootFolder=' + encodeURIComponent(options.rootFolder);
4730                    }
4731
4732                    if (options.setImageUrlOnUpload) {
4733                        if ($el.is('img')) {
4734                            $el.attr('src', getImageUrl);
4735                        }
4736                        else {
4737                            $el.attr('style', 'background-image:url("' + getImageUrl + '");background-size:cover;background-position:50%');
4738                        }
4739                    }
4740
4741                    $('#' + options.aRemove).show();
4742                    if (options.postbackScript) {
4743                        window.location = "javascript:" + options.postbackScript;
4744                    }
4745
4746                    if (options.doneFunction) {
4747                        options.doneFunction(e, data);
4748                    }
4749                },
4750                fail: function (e, data) {
4751                    var $el = $('#' + options.controlId).closest('.imageupload-group');
4752                    $el.siblings('.js-rockupload-alert').remove();
4753                    var $warning = $('<div class="alert alert-warning alert-dismissable js-rockupload-alert"/>');
4754
4755                    var msg = "unable to upload";
4756                    if (data.response().jqXHR && data.response().jqXHR.status == 406) {
4757                        msg = "file type not allowed";
4758                    } else if (data.response().jqXHR && data.response().jqXHR.responseText) {
4759                        msg = data.response().jqXHR.responseText;
4760                    }
4761
4762                    if (options.maxUploadBytes && data.total) {
4763                        if (data.total >= options.maxUploadBytes) {
4764                            msg = "file size is limited to " + (options.maxUploadBytes / 1024 / 1024) + "MB";
4765                        }
4766                    }
4767
4768                    $warning.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>')
4769                        .append('<strong><i class="ti ti-alert-triangle"></i> Warning </strong>')
4770                        .append(msg);
4771                    $warning.insertBefore($el);
4772                }
4773            });
4774
4775            $('#' + options.aRemove).on('click', function () {
4776                $(this).hide();
4777                var $el = $('#' + options.imgThumbnail);
4778                var noPictureUrl = options.noPictureUrl || Rock.settings.get('baseUrl') + 'Assets/Images/no-picture.svg';
4779                if ($el.is('img')) {
4780                    $el.attr('src', noPictureUrl);
4781                }
4782                else {
4783                    $el.attr('style', 'background-image:url(' + noPictureUrl + ');background-size:cover;background-position:50%');
4784                }
4785
4786                if (options.deleteFunction) {
4787                    options.deleteFunction();
4788                }
4789
4790                if (options.postbackRemovedScript) {
4791                    window.location = "javascript:" + options.postbackRemovedScript;
4792                } else {
4793                    $binaryFileId.val('0');
4794                    $contentFileSource.val('');
4795                }
4796                return false;
4797            });
4798        },
4799        exports = {
4800            initialize: function (options) {
4801                if (!options.controlId) throw 'Control ID must be set.';
4802                _configure(options);
4803            }
4804        };
4805
4806        return exports;
4807    }());
4808}(jQuery));
4809;
4810(function ($) {
4811    'use strict';
4812    window.Rock = window.Rock || {};
4813    Rock.controls = Rock.controls || {};
4814
4815    Rock.controls.itemPicker = (function () {
4816        var ItemPicker = function (options) {
4817            this.options = options;
4818
4819            // set a flag so that the picker only auto-scrolls to a selected item once. This prevents it from scrolling at unwanted times
4820            this.alreadyScrolledToSelected = false;
4821            this.iScroll = null;
4822        },
4823            exports;
4824
4825        ItemPicker.prototype = {
4826            constructor: ItemPicker,
4827            initialize: function () {
4828                var $control = $('#' + this.options.controlId),
4829                    $tree = $control.find('.treeview'),
4830                    treeOptions = {
4831                        multiselect: this.options.allowMultiSelect,
4832                        categorySelection: this.options.allowCategorySelection,
4833                        categoryPrefix: this.options.categoryPrefix,
4834                        restUrl: this.options.restUrl,
4835                        context: this.options.context,
4836                        restParams: this.options.restParams,
4837                        expandedIds: this.options.expandedIds,
4838                        expandedCategoryIds: this.options.expandedCategoryIds,
4839                        showSelectChildren: this.options.showSelectChildren,
4840                        id: this.options.startingId
4841                    },
4842                    $hfItemIds = $control.find('.js-item-id-value'),
4843                    $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'),
4844                    $hfExpandedCategoryIds = $control.find('.js-expanded-category-ids');
4845
4846                if (typeof this.options.mapItems === 'function') {
4847                    treeOptions.mapping = {
4848                        mapData: this.options.mapItems
4849                    };
4850                }
4851
4852                // clean up the tree (in case it was initialized already, but we are rebuilding it)
4853                var rockTree = $tree.data('rockTree');
4854                if (rockTree) {
4855                    rockTree.nodes = [];
4856                }
4857                $tree.empty();
4858
4859                var $scrollContainer = $control.find('.scroll-container .viewport');
4860                var $scrollIndicator = $control.find('.track');
4861                this.iScroll = new IScroll($scrollContainer[0], {
4862                    mouseWheel: true,
4863                    indicators: {
4864                        el: $scrollIndicator[0],
4865                        interactive: true,
4866                        resize: false,
4867                        listenY: true,
4868                        listenX: false,
4869                    },
4870                    click: false,
4871                    preventDefaultException: { tagName: /.*/ }
4872                });
4873
4874                // Since some handlers are "live" events, they need to be bound before tree is initialized
4875                this.initializeEventHandlers();
4876
4877                if ($hfItemIds.val() && $hfItemIds !== '0') {
4878                    treeOptions.selectedIds = $hfItemIds.val().split(',');
4879                }
4880
4881                if ($hfExpandedIds.val()) {
4882                    treeOptions.expandedIds = $hfExpandedIds.val().split(',');
4883                }
4884
4885                if ($hfExpandedCategoryIds.val()) {
4886                    treeOptions.expandedCategoryIds = $hfExpandedCategoryIds.val().split(',');
4887                }
4888
4889                if (this.options.universalItemPicker) {
4890                    function mapUniversalItems(data) {
4891                        return data.map(item => {
4892                            const treeItem = {
4893                                id: item.value,
4894                                name: item.text,
4895                                iconCssClass: item.iconCssClass,
4896                                hasChildren: item.hasChildren,
4897                                isCategory: false,
4898                                isSelectionDisabled: item.isSelectionDisabled,
4899                                childrenUrl: item.childrenUrl
4900                            };
4901
4902                            if (Array.isArray(item.children)) {
4903                                treeItem.children = mapUniversalItems(item.children);
4904                            }
4905
4906                            return treeItem;
4907                        });
4908                    }
4909
4910                    treeOptions.universalItemPicker = true;
4911                    treeOptions.expandedIds = (treeOptions.expandedIds || []).filter(id => id !== 0);
4912
4913                    treeOptions.getNodes = (parentId, parentNode, selectedIds, toExpandIds) => {
4914                        const req = {
4915                            context: treeOptions.context
4916                        };
4917
4918                        if (!parentId) {
4919                            req.expandToValues = (selectedIds || []).filter(a => a !== "0");
4920                        }
4921                        else {
4922                            req.parentValue = parentId;
4923                        }
4924
4925                        return $.ajax({
4926                            method: 'POST',
4927                            data: JSON.stringify(req),
4928                            url: treeOptions.restUrl,
4929                            dataType: 'json',
4930                            contentType: 'application/json'
4931                        })
4932                            .then(data => {
4933                                function checkItemsForExpansion(items, path) {
4934                                    for (let i = 0; i < items.length; i++) {
4935                                        if (selectedIds.some(id => id === items[i].value)) {
4936                                            for (let p = 0; p < path.length; p++) {
4937                                                if (!toExpandIds.includes(path[p])) {
4938                                                    toExpandIds.push(path[p]);
4939                                                }
4940                                            }
4941                                        }
4942
4943                                        if (Array.isArray(items[i].children)) {
4944                                            checkItemsForExpansion(items[i].children, [...path, items[i].value]);
4945                                        }
4946                                    }
4947                                }
4948
4949                                checkItemsForExpansion(data, []);
4950
4951                                return data;
4952                            });
4953                    };
4954
4955                    treeOptions.mapping = {
4956                        mapData: mapUniversalItems
4957                    };
4958                }
4959
4960                $tree.rockTree(treeOptions);
4961                this.updateScrollbar();
4962            },
4963            initializeEventHandlers: function () {
4964                var self = this,
4965                    $control = $('#' + this.options.controlId),
4966                    $spanNames = $control.find('.selected-names'),
4967                    $hfItemIds = $control.find('.js-item-id-value'),
4968                    $hfItemNames = $control.find('.js-item-name-value');
4969
4970                // Bind tree events
4971                $control.find('.treeview')
4972                    .on('rockTree:selected', function () {
4973                        // intentionally blank
4974                    })
4975                    .on('rockTree:itemClicked', function (e) {
4976                        // make sure it doesn't autoscroll after something has been manually clicked
4977                        self.alreadyScrolledToSelected = true;
4978                        if (!self.options.allowMultiSelect) {
4979                            $control.find('.picker-btn').trigger('click');
4980                        }
4981                    })
4982                    .on('rockTree:expand rockTree:collapse rockTree:dataBound', function (evt) {
4983                        self.updateScrollbar();
4984                    })
4985                    .on('rockTree:rendered', function (evt) {
4986                        self.scrollToSelectedItem();
4987                    });
4988
4989                $control.find('.picker-label').on('click', function (e) {
4990                    e.preventDefault();
4991                    $(this).toggleClass("active");
4992                    $control.find('.picker-menu').first().toggle(0, function () {
4993                        self.scrollToSelectedItem();
4994                    });
4995                });
4996
4997                $control.find('.picker-cancel').on('click', function () {
4998                    $(this).toggleClass("active");
4999                    $(this).closest('.picker-menu').toggle(0, function () {
5000                        self.updateScrollbar();
5001                    });
5002                    $(this).closest('.picker-label').toggleClass("active");
5003                });
5004
5005                // have the X appear on hover if something is selected
5006                if ($hfItemIds.val() && $hfItemIds.val() !== '0') {
5007                    $control.find('.picker-select-none').addClass('rollover-item');
5008                    $control.find('.picker-select-none').show();
5009                }
5010
5011                $control.find('.picker-btn').on('click', function (el) {
5012
5013                    var rockTree = $control.find('.treeview').data('rockTree'),
5014                        selectedNodes = rockTree.selectedNodes,
5015                        selectedIds = [],
5016                        selectedNames = [];
5017                    $.each(selectedNodes, function (index, node) {
5018                        var nodeName = $("<textarea/>").html(node.name).text();
5019                        selectedNames.push(nodeName);
5020                        if (!selectedIds.includes(node.id)) {
5021                            selectedIds.push(node.id);
5022                        }
5023                    });
5024
5025                    $hfItemIds.val(selectedIds.join(',')).trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
5026                    $hfItemNames.val(selectedNames.join(','));
5027
5028                    // have the X appear on hover. something is selected
5029                    $control.find('.picker-select-none').addClass('rollover-item');
5030                    $control.find('.picker-select-none').show();
5031
5032                    $spanNames.text(selectedNames.join(', '));
5033                    $spanNames.attr('title', $spanNames.text());
5034
5035                    $(this).closest('.picker-label').toggleClass("active");
5036                    $(this).closest('.picker-menu').toggle(0, function () {
5037                        self.updateScrollbar();
5038                    });
5039
5040                    $(this).trigger('onclick');
5041                    if (!(el && el.originalEvent && el.originalEvent.srcElement == this)) {
5042                        // if this event was called by something other than the button itself, make sure the execute the href (which is probably javascript)
5043                        var jsPostback = $(this).attr('href');
5044                        if (jsPostback) {
5045                            window.location = jsPostback;
5046                        }
5047                    }
5048                });
5049
5050                $control.find('.picker-select-none').on("click", function (e) {
5051                    e.preventDefault();
5052                    e.stopImmediatePropagation();
5053
5054                    var rockTree = $control.find('.treeview').data('rockTree');
5055                    rockTree.clear();
5056                    $hfItemIds.val('0').trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field.
5057                    $hfItemNames.val('');
5058
5059                    // don't have the X appear on hover. nothing is selected
5060                    $control.find('.picker-select-none').removeClass('rollover-item').hide();
5061
5062                    $control.siblings('.js-hide-on-select-none').hide();
5063
5064                    $spanNames.text(self.options.defaultText);
5065                    $spanNames.attr('title', $spanNames.text());
5066                    $(this).trigger('onclick');
5067                });
5068
5069                // clicking on the 'select all' btn
5070                $control.on('click', '.js-select-all', function (e)
5071                {
5072                  var rockTree = $control.find('.treeview').data('rockTree');
5073
5074                  e.preventDefault();
5075                  e.stopPropagation();
5076
5077                  var $itemNameNodes = rockTree.$el.find('.rocktree-name');
5078
5079                  var allItemNodesAlreadySelected = true;
5080                  $itemNameNodes.each(function (a)
5081                  {
5082                    if (!$(this).hasClass('selected')) {
5083                      allItemNodesAlreadySelected = false;
5084                    }
5085                  });
5086
5087                  if (!allItemNodesAlreadySelected) {
5088                    // mark them all as unselected (just in case some are selected already), then click them to select them
5089                    $itemNameNodes.removeClass('selected');
5090                    $itemNameNodes.trigger('click');
5091                  } else {
5092                    // if all were already selected, toggle them to unselected
5093                    rockTree.setSelected([]);
5094                    $itemNameNodes.removeClass('selected');
5095                  }
5096                });
5097            },
5098            updateScrollbar: function (sPosition) {
5099                var self = this;
5100                // first, update this control's scrollbar, then the modal'
5100s
5101                var $container = $('#' + this.options.controlId).find('.scroll-container');
5102
5103                if ($container.is(':visible')) {
5104                    if (!sPosition) {
5105                        sPosition = 'relative'
5106                    }
5107                    if (self.iScroll) {
5108                        self.iScroll.refresh();
5109                    }
5110                }
5111
5112                // update the outer modal
5113                Rock.dialogs.updateModalScrollBar(this.options.controlId);
5114            },
5115            scrollToSelectedItem: function () {
5116                var $selectedItem = $('#' + this.options.controlId).find('.picker-menu').find('.selected').first();
5117                if ($selectedItem.length && (!this.alreadyScrolledToSelected)) {
5118                    this.updateScrollbar();
5119                    this.iScroll.scrollToElement('.selected', '0s');
5120                    this.alreadyScrolledToSelected = true;
5121                } else {
5122                    // initialize/update the scrollbar
5123                    this.updateScrollbar();
5124                }
5125            }
5126        };
5127
5128        exports = {
5129            defaults: {
5130                id: 0,
5131                controlId: null,
5132                universalItemPicker: false,
5133                restUrl: null,
5134                restParams: null,
5135                allowCategorySelection: false,
5136                categoryPrefix: '',
5137                allowMultiSelect: false,
5138                defaultText: '',
5139                selectedIds: null,
5140                expandedIds: null,
5141                expandedCategoryIds: null,
5142                showSelectChildren: false
5143            },
5144            controls: {},
5145            initialize: function (options) {
5146                var settings,
5147                    itemPicker;
5148
5149                if (!options.controlId) throw 'controlId must be set';
5150                if (!options.restUrl) throw 'restUrl must be set';
5151
5152                settings = $.extend({}, exports.defaults, options);
5153
5154                if (!settings.defaultText) {
5155                    settings.defaultText = exports.defaults.defaultText;
5156                }
5157
5158                itemPicker = new ItemPicker(settings);
5159                exports.controls[settings.controlId] = itemPicker;
5160                itemPicker.initialize();
5161            }
5162        };
5163
5164        return exports;
5165    }());
5166}(jQuery));
5167;
5168; (function () {
5169    function updateKeyValues(e) {
5170        var $span = e.closest('span.key-value-list');
5171        var newValue = '';
5172
5173        $span.children('span.key-value-rows:first').children('div.controls-row').each(function (index) {
5174            if (newValue !== '') {
5175                newValue += '|';
5176            }
5177
5178            var keyValue = $(this).children('.key-value-key').first().val();
5179            var valueValue = $(this).children('.key-value-value').first().val();
5180
5181            // if the key or value have any magic chars ('^' or '|' or ','' ), URI encode each magic char using encodeURIComponent so that commas also get encoded
5182            var keyValueDelimiters = ['^', '|', ','];
5183            keyValueDelimiters.forEach(function (v, i, a) {
5184                var re = new RegExp('\\' + v, 'g');
5185                if (keyValue.indexOf(v) > -1) {
5186                    keyValue = keyValue.replace(re, encodeURIComponent(v));
5187                }
5188                if (valueValue.indexOf(v) > -1) {
5189                    valueValue = valueValue.replace(re, encodeURIComponent(v));
5190                }
5191            });
5192
5193            newValue += keyValue + '^' + valueValue;
5194        });
5195        $span.children('input').first().val(newValue);
5196
5197        raiseKeyValueList();
5198    }
5199
5200    function removeKeyValue(e) {
5201        var $rows = $(e).closest('span.key-value-rows');
5202        $(e).closest('div.controls-row').remove();
5203        Rock.controls.modal.updateSize($(e));
5204        updateKeyValues($rows);
5205    }
5206
5207    Sys.Application.add_load(function () {
5208
5209        $('a.key-value-add').on('click', function (e) {
5210            e.preventDefault();
5211            var $keyValueList = $(this).closest('.key-value-list');
5212            $keyValueList.find('.key-value-rows').append($keyValueList.find('.js-value-html').val());
5213            Rock.controls.modal.updateSize($(this));
5214            $('a.key-value-remove').off('click');
5215            $('a.key-value-remove').on('click', function (e) {
5216                e.preventDefault();
5217                removeKeyValue(this);
5218            });
5219            $('.js-key-value-input').off('change');
5220            $('.js-key-value-input').on('change', function (e) {
5221                e.preventDefault();
5222                updateKeyValues($(this));
5223            });
5224            updateKeyValues($(this));
5225        });
5226
5227        $('a.key-value-remove').off('click');
5228        $('a.key-value-remove').on('click', function (e) {
5229            e.preventDefault();
5230            removeKeyValue(this);
5231        });
5232
5233        $('.js-key-value-input').off('change');
5234        $('.js-key-value-input').on('change', function (e) {
5235            e.preventDefault();
5236            updateKeyValues($(this));
5237        });
5238    });
5239})();
5240;
5241(function ($) {
5242    'use strict';
5243    window.Rock = window.Rock || {};
5244    Rock.controls = Rock.controls || {};
5245
5246    Rock.controls.listItems = (function () {
5247        var exports = {
5248
5249            initialize: function (options) {
5250                if (!options.id) {
5251                    throw 'id is required';
5252                }
5253
5254                if (!options.clientId) {
5255                    throw 'clientId is required';
5256                }
5257
5258                const $listItemsControl = $('#' + options.clientId);
5259
5260                function updateListItemValues($e) {
5261                    var $span = $e.closest('span.list-items');
5262                    var keyValuePairs = [];
5263                    $span.children('span.list-items-rows').first().children('div.controls-row').each(function (index) {
5264                        keyValuePairs.push({
5265                            'Key': $(this).children('.input-group').find('.js-list-items-input').first().data('id'),
5266                            'Value': $(this).children('.input-group').find('.js-list-items-input').first().val()
5267                        });
5268                    });
5269                    $span.children('input').first().val(JSON.stringify(keyValuePairs));
5270                    if (options.valueChangedScript) {
5271                        window.location = "javascript:" + options.valueChangedScript;
5272                    }
5273
5274                }
5275
5276                var fixHelper = function (e, ui) {
5277                    ui.children().each(function () {
5278                        $(this).width($(this).width());
5279                    });
5280                    return ui;
5281                };
5282
5283                $listItemsControl.find('a.list-items-add').on('click', function (e) {
5284                    e.preventDefault();
5285                    $listItemsControl.find('.list-items-rows').append($listItemsControl.find('.js-list-items-html').val());
5286                    updateListItemValues($(this));
5287                    Rock.controls.modal.updateSize($(this));
5288                });
5289
5290                // Use add_load to fire with page load and postbacks
5291                Sys.Application.add_load(function () {
5292                    function onRemoveClick(event) {
5293                        event.preventDefault();
5294                        var $rows = $(this).closest('span.list-items-rows');
5295                        $(this).closest('div.controls-row').remove();
5296                        updateListItemValues($rows);
5297                        Rock.controls.modal.updateSize($(this));
5298                    }
5299
5300                    function onFocus(event) {
5301                        var element = event.target;
5302                        var $element = $(element);
5303                        var valueOnFocus = element.value;
5304
5305                        function onBlur() {
5306                            var valueOnBlur = element.value;
5307
5308                            if (valueOnFocus != valueOnBlur) {
5309                                updateListItemValues($element);
5310                            }
5311                        }
5312
5313                        // Only handle the focus out event once and remove the handler.
5314                        $element.one('blur', onBlur);
5315                    }
5316
5317                    $listItemsControl.find('a.list-items-remove').off('click', onRemoveClick).on('click', onRemoveClick);
5318                    $listItemsControl.find('.js-list-items-input').off('focus', onFocus).on('focus', onFocus);
5319                });
5320
5321                $listItemsControl.find('.list-items-rows').sortable({
5322                    helper: fixHelper,
5323                    handle: '.ti-menu-2',
5324                    start: function (event, ui) {
5325                        {
5326                            var start_pos = ui.item.index();
5327                            ui.item.data('start_pos', start_pos);
5328                        }
5329                    },
5330                    update: function (event, ui) {
5331                        updateListItemValues($(this));
5332                    }
5333
5334                }).disableSelection();
5335
5336            }
5337        };
5338
5339        return exports;
5340    }());
5341}(jQuery));
5342;
5343(function($) {
5344    window.Rock = window.Rock || {};
5345    window.Rock.controls = window.Rock.controls || {};
5346
5347    window.Rock.controls.mediaElementPicker = (function () {
5348        var exports = {
5349            initialize: function(options) {
5350                var $picker = $('#' + options.controlId);
5351
5352                $picker.data('required', options.required === true);
5353
5354                // Setup click handlers for any refresh buttons.
5355                $picker.find('.js-media-element-picker-refresh').on('click', function () {
5356                    if ($(this).hasClass('disabled')) {
5357                        return false;
5358                    }
5359
5360                    $picker.find('.js-media-element-picker-refresh').addClass('disabled');
5361                    $(this).find('i.ti').addClass('ti-spin');
5362
5363                    window.location = 'javascript:' + options.refreshScript;
5364
5365                    return false;
5366                });
5367
5368                // Re-validate after they change the selection of the media.
5369                $picker.find('.js-media-element-value select').on('change', function () {
5370                    ValidatorValidate(window[$picker.find('.js-media-element-validator').prop('id')]);
5371                });
5372            },
5373
5374            clientValidate: function (validator, args) {
5375                // After a postback, the validator is not actually in the DOM
5376                // anymore. So find the fake ancestor's Id and then lookup the
5377                // real media element picker.
5378                var mediaElementPickerId = $(validator).closest('.js-media-element-picker').attr('id');
5379                var $mediaElementPicker = $('#' + mediaElementPickerId);
5380
5381                var required = $mediaElementPicker.data('required') === true;
5382                var value = $mediaElementPicker.find('.js-media-element-value select').val() || '';
5383                var isValid = !required || (value !== '' && value !== '0');
5384
5385                if (isValid) {
5386                    $mediaElementPicker.closest('.form-group').removeClass('has-error');
5387                    args.IsValid = true;
5388                }
5389                else {
5390                    $mediaElementPicker.closest('.form-group').addClass('has-error');
5391                    args.IsValid = false;
5392                }
5393            }
5394        };
5395
5396        return exports;
5397    })();
5398})(jQuery);
5399;
5400(function ($) {
5401    'use strict';
5402    window.Rock = window.Rock || {};
5403    Rock.controls = Rock.controls || {};
5404
5405    Rock.controls.mediaPlayer = (function () {
5406        var exports = {
5407            initialize: function () {
5408
5409                Sys.Application.add_load(function () {
5410                    var cssFile = Rock.settings.get('baseUrl') + 'Scripts/mediaelementjs/mediaelementplayer.min.css';
5411                    var jsFile = Rock.settings.get('baseUrl') + 'Scripts/mediaelementjs/mediaelement-and-player.js';
5412
5413                    // ensure that css for mediaelementplayers is added to page
5414                    if (!$('#mediaElementCss').length) {
5415                        $('head').append("<link id='mediaElementCss' href='" + cssFile + "' type='text/css' rel='stylesheet' />");
5416                    }
5417
5418                    // ensure that js for mediaelementplayers is added to page
5419                    if (!$('#mediaElementJs').length) {
5420                        // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this
5421                        $.ajaxSetup({ cache: true });
5422                        $('head').prepend("<script id='mediaElementJs' src='" + jsFile + "' />");
5423                    }
5424
5425                    // 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)
5426                    $('audio.js-media-audio,video.js-media-video').mediaelementplayer({ enableAutosize: true });
5427                });
5428
5429            }
5430        };
5431
5432        return exports;
5433    }());
5434
5435    /** This is the new Rock media player that uses Plyr. */
5436    Rock.controls.mediaplayer = (function () {
5437        var exports = {
5438            initialize: function (options) {
5439                if (!options.id) {
5440                    throw "id is required";
5441                }
5442
5443                if (!options.playerId) {
5444                    throw "playerId is required";
5445                }
5446
5447                if (!options.progressId) {
5448                    throw "progressId is required";
5449                }
5450
5451                var $playerElement = $("#" + options.id);
5452                var playerOptions = $playerElement.data("player-options");
5453
5454                var player = new Rock.UI.MediaPlayer("#" + options.playerId, playerOptions);
5455
5456                player.on("ready", function () {
5457                    $("#" + options.progressId).val(player.percentWatched);
5458                });
5459
5460                player.on("progress", function () {
5461                    console.log("progress", player.percentWatched, options.progressId);
5462                    $("#" + options.progressId).val(player.percentWatched);
5463                });
5464
5465                $playerElement
5466                    .data("required", options.required || false)
5467                    .data("progress-id", options.progressId)
5468                    .data("required-percentage", options.requiredPercentage || 0)
5469                    .data("required-error-message", options.requiredErrorMessage || "You must watch the video.");
5470            },
5471
5472            clientValidate: function (validator, args) {
5473                var $playerElement = $(validator).closest(".js-media-player");
5474                var $formGroup = $playerElement.parent().hasClass("control-wrapper") ? $playerElement.closest(".form-group") : null;
5475                var progressId = $playerElement.data("progress-id");
5476                var required = $playerElement.data('required');
5477                var requiredProgress = $playerElement.data("required-percentage");
5478                var progress = parseFloat($("#" + progressId).val() || "0");
5479
5480                var isValid = !required || progress >= requiredProgress;
5481
5482                if (isValid) {
5483                    if ($formGroup) {
5484                        $formGroup.removeClass("has-error");
5485                    }
5486
5487                    args.IsValid = true;
5488                }
5489                else {
5490                    if ($formGroup) {
5491                        $formGroup.addClass("has-error");
5492                    }
5493
5494                    args.IsValid = false;
5495                    validator.errormessage = $playerElement.data('required-error-message');
5496                }
5497            }
5498        };
5499
5500        return exports;
5501    }());
5502}(jQuery));
5503;
5504(function ($) {
5505    'use strict';
5506    window.Rock = window.Rock || {};
5507    Rock.controls = Rock.controls || {};
5508
5509    Rock.controls.mediaSelector = (function () {
5510
5511        var exports = {
5512            clientValidate: function (validator, args) {
5513                var $checkBoxList = $(validator).closest('.js-mediaselector');
5514
5515                var checkboxes = $checkBoxList.find('input');
5516                var isValid = false;
5517                for (var i = 0; i < checkboxes.length; i++) {
5518                    if (checkboxes[i].checked) {
5519                        isValid = true;
5520                        break;
5521                    }
5522                }
5523
5524                if (isValid) {
5525                    $checkBoxList.removeClass('has-error');
5526                } else {
5527                    $checkBoxList.addClass('has-error');
5528                }
5529
5530                args.IsValid = isValid;
5531            }
5532        };
5533
5534        return exports;
5535    }());
5536}(jQuery));
5537
5538
5539;
5540/*
5541 * BJW  7-7-2020
5542 * Previous Issues and Fixes
5543 *
5544 * April 15, 2020
5545 * Problem:
5546 *      Open a modal dialog and close it. Next, try to open a modal alert. An invisible backdrop is added to the
5547 *      page such that the page cannot be interacted with. The alert does not appear.
5548 * Solution:
5549 *      Every postback causes the C# modal control code to register JS that checks a hidden field to determine
5550 *      if a modal should be open or closed. If many modals exist, then they are all checked and all call
5551 *      the JS close dialog method, which removed backdrops. If each modal removes any backdrop without
5552 *      discretion, then they interfere with each other. Therefore, only remove backdrops that belong to the modal
5553 *      in current context being closed.
5554 *      https://github.com/SparkDevNetwork/Rock/commit/8a1c5653c534c61548de34d0ca1a4483304437a1
5555 *
5556 * June 1, 2020
5557 * Problem:
5558 *      When a modal is opened, sometimes the body is set to .modal-open and sometimes a manager (usually the
5559 *      update panel of the block) is set to .modal-open. In the event of the manager, cleanup was not occurring
5560 *      and the class remained even after the modal was gone.
5561 * Solution:
5562 *      A param was added to the close dialog JS method that allowed proper cleanup of classes (.modal-open) added
5563 *      to the manager.
5564 *      https://github.com/SparkDevNetwork/Rock/commit/26cf101ae8e8923008ae42f3d833cab5a35df8cb
5565 *
5566 * July 1, 2020
5567 * Problem:
5568 *      Open a dialog. Close the dialog with a postback that calls C# Hide with no manager param. Open the dialog again
5569 *      and there is no backdrop.
5570 * Solution:
5571 *      This seems to be an issue with bootstrap (seems unlikely) or the combination of bootstrap with the postback
5572 *      UI re-rendering. The modal is removed from the DOM before our JS close method is reached. Therefore proper
5573 *      cleanup of the first modal open is never done. This seems to impact the second opening and bootstrap fails
5574 *      to add the backdrop. Thus our code adds a backdrop if one is not visible.
5575 *      https://github.com/SparkDevNetwork/Rock/commit/f08bb6fc1e17b79d0772152707acd4a3876a68d6
5576 *
5577 *  July 7, 2020
5578 *  Problem:
5579 *      Open a dialog. Close the dialog with a postback that calls C# Hide with no manager param. Open the dialog again
5580 *      and there is a backdrop but no modal.
5581 *  Solution:
5582 *      This is because the postback calls the C# Hide method, which does not provide the manager to the JS close modal
5583 *      method. Because of this, proper cleanup doesn't occur. We can calculate the manger as we did when we opened the
5584 *      modal, which fixes the issue.
5585 *      Also, there was a bug in hiding the backdrop where if the owner no longer existed
5586 *      in the DOM, then the backdrop was not removed.
5587 */
5588
5589(function ($) {
5590    'use strict';
5591    window.Rock = window.Rock || {};
5592    Rock.controls = Rock.controls || {};
5593
5594    Rock.controls.modal = (function () {
5595        // shows the IFrame #modal-popup modal
5596        var _showModalPopup = function (sender, popupUrl) {
5597            var $modalPopup = $('#modal-popup');
5598            var $modalPopupIFrame = $modalPopup.find('iframe');
5599
5600            // Use the anchor tag's title attribute as the title of the dialog box
5601            if (sender && sender.attr('title') != undefined) {
5602                $('#modal-popup_panel h3').html(sender.attr('title') + ' <small></small>');
5603            }
5604
5605            $modalPopupIFrame.height('auto');
5606
5607            $modalPopupIFrame.one('load', function () {
5608
5609                // now that the iframe is loaded, show it, set it's initial height and do a modal layout
5610                $('#modal-popup').fadeTo(0, 1);
5611
5612                var newHeight = $(this.contentWindow.document).height();
5613                if ($(this).height() != newHeight) {
5614                    $(this).height(newHeight);
5615                }
5616
5617                $('body').addClass('modal-open');
5618                if (document.body.clientHeight > window.innerHeight) {
5619                    $('body').css('padding-right', Rock.controls.util.getScrollbarWidth());
5620                }
5621                $('#modal-popup').modal('layout');
5622            });
5623
5624            // Use the anchor tag's href attribute as the source for the iframe
5625            // this will trigger the load event (above) which will show the popup
5626            $('#modal-popup').fadeTo(0, 0);
5627
5628            // Indicate to Rock that this content is being served in an iFrame modal.
5629            var separator = popupUrl.indexOf("?") === -1 ? "?" : "&";
5630            popupUrl = popupUrl + separator + "IsIFrameModal=true";
5631
5632            $modalPopupIFrame.attr('src', popupUrl);
5633            $('#modal-popup').modal({
5634                show: true,
5635                backdrop: 'static',
5636                keyboard: false,
5637                attentionAnimation: '',
5638                modalOverflow: true
5639            });
5640        }
5641
5642        // Closes the non-iframe modal dialog control
5643        var _closeModalControl = function ($modalDialog, $manager) {
5644            if ($modalDialog && $modalDialog.length && $modalDialog.modal) {
5645                $modalDialog.modal('hide');
5646            }
5647
5648            // remove the modal-open class from this modal's manager
5649            if ($manager && $manager.length) {
5650                $manager.each(function () {
5651                    $(this).removeClass('modal-open');
5652                });
5653            }
5654
5655            // if all modals are closed, remove the modal-open class from the body
5656            if ($('.modal:visible').length === 0) {
5657                $('body').removeClass('modal-open').css('padding-right', '');
5658            }
5659
5660            // Ensure any modalBackdrops are removed if its owner is no longer visible. Note that some
5661            // backdrops do not have an owner
5662            $('.modal-backdrop').each(function () {
5663                var $modalBackdrop = $(this);
5664                var ownerId = $modalBackdrop.data('modalId');
5665                var $owner = $('#' + ownerId);
5666                var isOwnerInDom = $owner.length > 0;
5667
5668                if (ownerId && (!isOwnerInDom || !$owner.is(':visible'))) {
5669                    $modalBackdrop.remove();
5670                }
5671            });
5672        };
5673
5674        // shows a non-IFrame modal dialog control
5675        var _showModalControl = function ($modalDialog, managerId, clickBackdropToClose, $hfModalVisible) {
5676            $('body').addClass('modal-open');
5677            if (document.body.clientHeight > window.innerHeight) {
5678                $('body').css('padding-right', Rock.controls.util.getScrollbarWidth());
5679            }
5680
5681            $modalDialog.modal({
5682                show: true,
5683                manager: managerId,
5684                backdrop: 'static',
5685                keyboard: false,
5686                attentionAnimation: '',
5687                modalOverflow: true,
5688                replace: true
5689            });
5690
5691            if ($('.modal-backdrop').filter(':visible').length === 0) {
5692                // 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
5693                $('<div class="modal-backdrop" data-modal-id="' + $modalDialog.prop('id') + '" />').appendTo('body');
5694            }
5695
5696            if (clickBackdropToClose) {
5697                $(".modal-scrollable").click(function (event) {
5698                    if (event.target === event.currentTarget) {
5699                        $(".modal-scrollable").off("click");
5700                        _closeModalControl($modalDialog, $(managerId));
5701
5702                        if ($hfModalVisible && $hfModalVisible.length) {
5703                            $hfModalVisible.val(0);
5704                        }
5705                    }
5706                });
5707            }
5708        }
5709
5710        var exports = {
5711            // updates the side of the modal that the control is in.
5712            // this function works for both the IFrame modal and ModalDialog control
5713            updateSize: function (controlId) {
5714                var $control = typeof (controlId) == 'string' ? $('#' + controlId) : $(controlId);
5715                if ($control && $control.length) {
5716                    var $modalBody = $control.closest('.modal-body');
5717                    if ($modalBody.is(':visible')) {
5718                        $modalBody[0].style.minHeight = "0";
5719                        var scrollHeight = $modalBody.prop('scrollHeight');
5720                        if ($modalBody.outerHeight() != scrollHeight) {
5721                            // if modalbody didn't already grow to fit (maybe because of a bootstrap dropdown) make modal-body big enough to fit.
5722                            $modalBody[0].style.minHeight = scrollHeight + "px";
5723
5724                            // force the resizeDetector to fire
5725                            if ($('#dialog').length && $('#dialog')[0].resizedAttached) {
5726                                $('#dialog')[0].resizedAttached.call();
5727                            }
5728                        }
5729                    }
5730                }
5731
5732            },
5733            // closes the #modal-popup modal (IFrame Modal)
5734            close: function (msg) {
5735                // do a setTimeout so this fires after the postback
5736                $('#modal-popup').hide();
5737                setTimeout(function () {
5738                    $('#modal-popup iframe').attr('src', '');
5739                    $('#modal-popup').modal('hide');
5740
5741                }, 0);
5742
5743                $('body').removeClass('modal-open').css('padding-right', '');
5744
5745                if (msg && msg != '') {
5746
5747                    if (msg == 'PAGE_UPDATED') {
5748                        location.reload(true);
5749                    }
5750                    else {
5751                        $('#rock-config-trigger-data').val(msg);
5752                        $('#rock-config-trigger').trigger('click');
5753                    }
5754                }
5755            },
5756            // closes a ModalDialog control (non-IFrame Modal)
5757            closeModalDialog: function ($modalDialog, $manager) {
5758                _closeModalControl($modalDialog, $manager);
5759            },
5760            // shows the #modal-popup modal (IFrame Modal)
5761            show: function (sender, popupUrl, detailsId, postbackUrl) {
5762                _showModalPopup(sender, popupUrl);
5763            },
5764            // shows a ModalDialog control (non-IFrame Modal)
5765            showModalDialog: function ($modalDialog, managerId, clickBackdropToClose, $hfModalVisible) {
5766                _showModalControl($modalDialog, managerId, clickBackdropToClose, $hfModalVisible);
5767            },
5768
5769            // gets the IFrame element of the global the Modal Popup (for the IFrame Modal)
5770            getModalPopupIFrame: function () {
5771                var $modalPopup = $('#modal-popup');
5772                var $modalPopupIFrame = $modalPopup.find('iframe');
5773
5774                return $modalPopupIFrame;
5775            }
5776        };
5777
5778        return exports;
5779    }());
5780}(jQuery));
5781;
5782(function ($) {
5783    'use strict';
5784    window.Rock = window.Rock || {};
5785    Rock.controls = Rock.controls || {};
5786
5787    /** JS helper for the NoteEditor control */
5788    Rock.controls.noteEditor = (function () {
5789        var exports = {
5790            /** initializes the JavasSript for the noteEditor control */
5791            initialize: function (options) {
5792
5793                if (!options.id) {
5794                    throw 'id is required';
5795                }
5796
5797                var self = this;
5798
5799                var $control = $('#' + options.id);
5800
5801                if ($control.length == 0) {
5802                    return;
5803                }
5804
5805                self.$noteEditor = $control;
5806
5807                this.initializeEventHandlers();
5808
5809                if (options.isEditing) {
5810                    var $noteContainer = self.$noteEditor.closest('.js-notecontainer');
5811
5812                    if (options.currentNoteId) {
5813                        // editing an existing note
5814                        var $displayedNote = $noteContainer.find("[data-note-id='" + options.currentNoteId + "']");
5815
5816                        // move note editor and display in place of the readonly version of note
5817                        self.$noteEditor.detach();
5818                        $displayedNote.parent('.js-note').prepend(self.$noteEditor);
5819                        self.$noteEditor.fadeIn();
5820
5821                        $displayedNote.hide();
5822                    }
5823                    else if (options.parentNoteId) {
5824                        // new reply to a note
5825                        var $replyToNote = $noteContainer.find("[data-note-id='" + options.parentNoteId + "']");
5826
5827                        // move note editor as a child note of the note we are replying to
5828                        self.$noteEditor.detach();
5829                        $replyToNote.append(self.$noteEditor)
5830                        self.$noteEditor.slideDown().find('textarea').trigger("focus");
5831                    }
5832                    else {
5833                        // new note, so just show it 
5834                        self.$noteEditor.fadeIn();
5835                    }
5836                }
5837            },
5838
5839            /**  */
5840            initializeEventHandlers: function () {
5841                // Initialize NoteEditor and NoteContainer events
5842                var self = this;
5843                var $noteContainer = self.$noteEditor.closest('.js-notecontainer');
5844
5845                $('.js-addnote,.js-editnote,.js-replynote', $noteContainer).on('click', function (e) {
5846                    var addNote = $(this).hasClass('js-addnote');
5847                    var editNote = $(this).hasClass('js-editnote');
5848                    var replyNote = $(this).hasClass('js-replynote');
5849                    var cancelNote = $(this).hasClass('js-editnote-cancel');
5850                    var deleteNote = $(this).hasClass('js-removenote');
5851
5852                    var sortDirection = $noteContainer.data('sortdirection');
5853                    var $noteEditor = $noteContainer.find('.js-note-editor');
5854                    var $currentNote = $(false);
5855                    $noteEditor.detach();
5856
5857                    // clear out any previously entered stuff
5858                    $noteEditor.find('.js-parentnoteid').val('');
5859                    $noteEditor.find('textarea').val('');
5860                    $noteEditor.find('input:checkbox').prop('checked', false);
5861                    $noteEditor.find('.js-notesecurity').hide();
5862
5863                    var $noteprivateInput = $noteEditor.find('.js-noteprivate');
5864                    $noteprivateInput.parent().show();
5865
5866                    if (addNote) {
5867                        e.preventDefault();
5868                        e.stopImmediatePropagation();
5869                        var postbackJs = $noteContainer.find(".js-add-postback").attr('href');
5870                        window.location = postbackJs;
5871                        return;
5872                    }
5873                    else {
5874                        $currentNote = $(this).closest('.js-noteviewitem');
5875                        var currentNoteId = $currentNote.data('note-id');
5876
5877                        if (replyNote) {
5878                            $noteContainer.find('.js-currentnoteid').val(currentNoteId);
5879                            $noteEditor.find('.js-parentnoteid').val(currentNoteId);
5880
5881                            e.preventDefault();
5882                            e.stopImmediatePropagation();
5883                            var postbackJs = $noteContainer.find(".js-reply-to-postback").attr('href');
5884                            window.location = postbackJs;
5885                            return;
5886                        }
5887                        else if (editNote) {
5888                            $noteContainer.find('.js-currentnoteid').val(currentNoteId);
5889
5890                            e.preventDefault();
5891                            e.stopImmediatePropagation();
5892                            var postbackJs = $noteContainer.find(".js-edit-postback").attr('href');
5893                            window.location = postbackJs;
5894                            return;
5895                        }
5896                    }
5897                });
5898
5899                $('.js-notesecurity', $noteContainer).on('click', function (e) {
5900                    var $securityBtn = $(this);
5901                    var entityTypeId = $securityBtn.data('entitytype-id');
5902                    var title = $securityBtn.data('title');
5903                    var currentNoteId = $securityBtn.data('entity-id');
5904                    var securityUrl = Rock.settings.get('baseUrl') + "Secure/" + entityTypeId + "/" + currentNoteId + "?t=" + title + "&pb=&sb=Done";
5905                    Rock.controls.modal.show($securityBtn, securityUrl);
5906                });
5907
5908                $('.js-editnote-cancel', $noteContainer).on('click', function (e) {
5909                    var $noteEditor = $noteContainer.find('.js-note-editor');
5910                    $noteEditor.slideUp();
5911
5912                    // show any notedetails that might have been hidden when doing the editing
5913                    $noteEditor.parent().find('.js-noteviewitem').slideDown();
5914                });
5915
5916                $('.js-removenote', $noteContainer).on('click', function (e) {
5917                    var $currentNote = $(this).closest('.js-noteviewitem');
5918                    var currentNoteId = $currentNote.attr('data-note-id');
5919                    $noteContainer.find('.js-currentnoteid').val(currentNoteId);
5920
5921                    e.preventDefault();
5922                    e.stopImmediatePropagation();
5923                    var postbackJs = $noteContainer.find(".js-delete-postback").attr('href');
5924                    return Rock.dialogs.confirm('Are you sure you want to delete this note?', function (result) {
5925                        if (result) {
5926                            window.location = postbackJs;
5927                        }
5928                    });
5929                });
5930
5931                $('.js-expandreply', $noteContainer).on('click', function (e) {
5932                    var $currentNote = $(this).closest('.js-note');
5933                    var $childNotesContainer = $currentNote.find('.js-childnotes').first();
5934                    $childNotesContainer.slideToggle(function (x) {
5935
5936                        // get a list of noteIds that have their child items visible, so that we can maintain that expansion after a postback
5937                        var expandedNoteIds = $(this).closest('.js-notec
5937ontainer').find('.js-noteviewitem:visible').map(function () {
5938                            var $noteItem = $(this).closest('.js-note');
5939                            var $childNotesExpanded = $noteItem.find('.js-childnotes:first').is(':visible');
5940                            if ($childNotesExpanded) {
5941                                return $(this).attr('data-note-id');
5942                            }
5943                            else {
5944                                return null;
5945                            }
5946                        }).get().join();
5947
5948                        var $expandedNoteIdsHiddenField = $noteContainer.find('.js-expandednoteids');
5949                        $expandedNoteIdsHiddenField.val(expandedNoteIds);
5950                    });
5951                });
5952            }
5953        }
5954
5955        return exports
5956    }());
5957}(jQuery));
5958;
5959(function ($) {
5960    'use strict';
5961    window.Rock = window.Rock || {};
5962    Rock.controls = Rock.controls || {};
5963
5964    Rock.controls.numberBox = (function () {
5965        var exports = {
5966            clientValidate: function (validator, args) {
5967                var validationControl = $(validator);
5968                var numberType = validationControl.attr('type');
5969
5970                // Only validate numeric types - currency and other formats require server validation.
5971                if (!(numberType === 'double' || numberType === 'integer')) {
5972                    return;
5973                }
5974
5975                var $numberBox = $(validator).closest('.js-number-box');
5976                var isValid = true;
5977                var validationMessage = '';
5978
5979                var label = $numberBox.find('label').text();
5980                if (!label) {
5981                    label = "Value";
5982                }
5983
5984                var value = args.Value;
5985
5986                // Check for a valid number - allow a signed or unsigned decimal with or without digit separators.
5987                var floatRegex = /(?=.*?\d)^[-,+]?(([1-9]\d{0,2}(,\d{3})*)|\d+)?(\.\d{1,9})?$/s;
5988                validationMessage = label + " must be a valid number.";
5989                isValid = floatRegex.test(value);
5990
5991                // Get the numeric value.
5992                value = parseFloat(value);
5993
5994                // Check for integer.
5995                if (isValid) {
5996                    if (numberType === "integer") {
5997                        validationMessage = label + " must be an integer value.";
5998                        isValid = Number.isInteger(value);
5999                    }
6000                }
6001
6002                // Check range.
6003                if (isValid) {
6004                    var max = parseFloat(validationControl.attr("max"));
6005                    var min = parseFloat(validationControl.attr("min"));
6006                    var checkMaxValue = !Number.isNaN( max );
6007                    var checkMinValue = !Number.isNaN( min );
6008
6009                    if ( checkMinValue && checkMaxValue) {
6010                        validationMessage = label + ' must have a value between ' + min + ' and ' + max + '.';
6011                        isValid = (value >= min && value <= max);
6012                    }
6013                    else if (checkMinValue) {
6014                        validationMessage = label + ' must have a value of ' + min + ' or more.';
6015                        isValid = (value >= min);
6016                    }
6017                    else if (checkMaxValue) {
6018                        validationMessage = label + ' must have a value of ' + max + ' or less.';
6019                        isValid = (value <= max);
6020                    }
6021                }
6022
6023                args.IsValid = isValid;
6024
6025                // Set the visual feedback.
6026                if (isValid) {
6027                    $numberBox.removeClass('has-error');
6028                    $numberBox.attr('title', '');
6029                }
6030                else {
6031                    $numberBox.addClass('has-error');
6032                    $numberBox.attr('title', validationMessage);
6033
6034                    validator.errormessage = validationMessage;
6035                    validator.isvalid = false;
6036                }
6037            }
6038        };
6039
6040        return exports;
6041    }());
6042}(jQuery));
6043;
6044(function ($) {
6045    'use strict';
6046    window.Rock = window.Rock || {};
6047    Rock.controls = Rock.controls || {};
6048
6049    Rock.controls.numberRangeEditor = (function () {
6050        var exports = {
6051            clientValidate: function (validator, args) {
6052                var $numberRangeEditor = $(validator).closest('.js-numberrangeeditor');
6053                var lowerValue = $numberRangeEditor.find('input.js-number-range-lower ').val();
6054                var upperValue = $numberRangeEditor.find('input.js-number-range-upper').val();
6055                var required = $numberRangeEditor.attr('data-required') == 'true';
6056                var itemLabelText = $numberRangeEditor.attr('data-itemlabel');
6057
6058                var isValid = true;
6059
6060                if (required) {
6061                    // if required, then make sure that the number range has a lower and/or upper value (can't both be blank)
6062                    if (lowerValue.length === 0 && upperValue.length === 0) {
6063                        isValid = false;
6064                        validator.errormessage = itemLabelText + ' is required';
6065                    }
6066                }
6067
6068                var control = $numberRangeEditor;
6069                var labelForSelector = "label[for='" + $numberRangeEditor.prop('id') + "']";
6070                var $labelFor = $(labelForSelector)
6071                if (isValid) {
6072                    control.removeClass('has-error');
6073                    $labelFor.parent().removeClass('has-error');
6074                }
6075                else {
6076                    control.addClass('has-error');
6077                    $labelFor.parent().addClass('has-error');
6078                }
6079
6080                args.IsValid = isValid;
6081            }
6082        };
6083
6084        return exports;
6085    }());
6086}(jQuery));
6087;
6088(function ($) {
6089    'use strict';
6090    window.Rock = window.Rock || {};
6091    Rock.controls = Rock.controls || {};
6092
6093    Rock.controls.numberUpDown = (function () {
6094
6095        var exports = {
6096            adjust: function (btn, adjustment, postbackScript) {
6097
6098                var $parent = $(btn).closest('div.numberincrement');
6099                var $min = $parent.find('.js-number-up-down-min').first();
6100                var $max = $parent.find('.js-number-up-down-max').first();
6101                var $value = $parent.find('.js-number-up-down-value').first();
6102                var $lbl = $parent.find('.js-number-up-down-lbl').first();
6103                var $upBtn = $parent.find('.js-number-up').first();
6104                var $downBtn = $parent.find('.js-number-down').first();
6105
6106                // Get the min, max, and new value
6107                var minValue = parseInt($min.val(), 10);
6108                var maxValue = parseInt($max.val(), 10);
6109                var numValue = parseInt($value.val(), 10) + adjustment;
6110
6111                // If new value is valid, set the hf and lbl
6112                if (numValue >= minValue && numValue <= maxValue) {
6113                    $value.val(numValue);
6114                    $lbl.html(numValue);
6115                }
6116
6117                // enable/disable the 'up' button
6118                if (numValue >= maxValue) {
6119                    $upBtn.addClass('disabled');
6120                } else {
6121                    $upBtn.removeClass('disabled');
6122                }
6123
6124                // enable/disable the 'down' button
6125                if (numValue <= minValue) {
6126                    $downBtn.addClass('disabled');
6127                } else {
6128                    $downBtn.removeClass('disabled');
6129                }
6130
6131                if (postbackScript && postbackScript != "" ) {
6132                    window.location = "javascript:" + postbackScript;
6133                }
6134
6135            }
6136        };
6137
6138        return exports;
6139    }());
6140}(jQuery));;
6141(function ($)
6142{
6143  'use strict';
6144  window.Rock = window.Rock || {};
6145  Rock.controls = Rock.controls || {};
6146
6147  Rock.controls.numberUpDownGroup = (function ()
6148  {
6149    var exports = {
6150      clientValidate: function (validator, args)
6151      {
6152        var $numberUpDownGroup = $(validator).closest('.js-number-up-down-group');
6153        var isValid = true;
6154
6155        if ($numberUpDownGroup.hasClass('required') === true) {
6156          isValid = false;
6157
6158          $numberUpDownGroup.find('.js-number-up-down-lbl').each(function (i) {
6159            if (parseInt(this.innerText, 10) > 0) {
6160              isValid = true;
6161            }
6162          });
6163        }
6164
6165        if (isValid === false) {
6166          validator.errormessage = $numberUpDownGroup.find('label').text() + " is required";
6167        }
6168
6169        var control = $numberUpDownGroup;
6170        if (isValid)
6171        {
6172          control.removeClass('has-error');
6173        }
6174        else
6175        {
6176          control.addClass('has-error');
6177        }
6178
6179        args.IsValid = isValid;
6180      }
6181    };
6182
6183    return exports;
6184  }());
6185}(jQuery));
6186;
6187(function ($) {
6188    'use strict';
6189    window.Rock = window.Rock || {};
6190    Rock.controls = Rock.controls || {};
6191
6192    Rock.controls.pbx = (function () {
6193
6194        var exports = {
6195            // implements the click to call feature
6196            originate: function (sourcePersonGuid, destinationNumber, callerId, destinationPersonName, destinationNumberFormatted) {
6197                var originateUrl = Rock.settings.get('baseUrl') + 'api/Pbx/Originate?sourcePersonGuid=' + sourcePersonGuid + '&destinationPhone=' + destinationNumber + '&callerId=' + callerId;
6198
6199                console.log('From Rock.controls.originateCall: ' + originateUrl);
6200                $.get(originateUrl, function (r) {
6201                    if (!r.Success) {
6202                        Rock.dialogs.alert("An error occurred while attempting to place call.  " + r.Message);
6203                    }
6204                });
6205            }
6206        };
6207
6208        return exports;
6209    }());
6210}(jQuery));;
6211(function ($) {
6212    'use strict';
6213    window.Rock = window.Rock || {};
6214    Rock.controls = Rock.controls || {};
6215
6216    Rock.controls.personPicker = (function () {
6217        var PersonPicker = function (options) {
6218            this.controlId = options.controlId;
6219            this.restUrl = options.restUrl;
6220            this.restDetailUrl = options.restDetailUrl;
6221            this.defaultText = options.defaultText || '';
6222            this.iScroll = null;
6223            this.$pickerControl = $('#' + this.controlId);
6224            this.$pickerScrollContainer = this.$pickerControl.find('.js-personpicker-scroll-container');
6225        };
6226
6227        PersonPicker.prototype.initializeEventHandlers = function () {
6228            var controlId = this.controlId,
6229                restUrl = this.restUrl,
6230                restDetailUrl = this.restDetailUrl || (Rock.settings.get('baseUrl') + 'api/People/GetSearchDetails'),
6231                defaultText = this.defaultText;
6232
6233            var $pickerControl = this.$pickerControl;
6234            var $searchFields = $pickerControl.find('.js-personpicker-search-field input');
6235            var $searchFieldName = $pickerControl.find('.js-personpicker-search-name input');
6236            var $searchFieldAddress = $pickerControl.find('.js-personpicker-search-address input');
6237            var $searchFieldPhone = $pickerControl.find('.js-personpicker-search-phone input');
6238            var $searchFieldEmail = $pickerControl.find('.js-personpicker-search-email input');
6239            var $searchResults = $pickerControl.find('.js-personpicker-searchresults');
6240            var $pickerToggle = $pickerControl.find('.js-personpicker-toggle');
6241            var $pickerMenu = $pickerControl.find('.js-personpicker-menu');
6242            var $pickerSelect = $pickerControl.find('.js-personpicker-select');
6243            var $pickerSelectNone = $pickerControl.find('.js-picker-select-none');
6244            var $pickerPersonId = $pickerControl.find('.js-person-id');
6245            var $pickerPersonName = $pickerControl.find('.js-person-name');
6246            var $pickerCancel = $pickerControl.find('.js-personpicker-cancel');
6247            var $pickerToggleAdditionalSearchFields = $pickerControl.find('.js-toggle-additional-search-fields');
6248            var $pickerAdditionalSearchFields = $pickerControl.find('.js-personpicker-additional-search-fields');
6249            var $pickerExpandSearchFields = $pickerControl.find('.js-expand-search-fields');
6250
6251            var includeBusinesses = $pickerControl.find('.js-include-businesses').val() == '1' ? 'true' : 'false';
6252            var includeDeceased = $pickerControl.find('.js-include-deceased').val() == '1' ? 'true' : 'false';
6253            var includeDetails = 'true';
6254
6255            var promise = null;
6256            var lastSelectedPersonId = null;
6257
6258            var autoCompletes = $searchFields.autocomplete({
6259                source: function (request, response) {
6260
6261                    var search = {
6262                        name: $searchFieldName.val(),
6263                        address: $searchFieldAddress.val(),
6264                        phone: $searchFieldPhone.val(),
6265                        email: $searchFieldEmail.val()
6266                    };
6267
6268                    // make sure that at least one of the search f
6268ields has 3 chars in it
6269                    if ((search.name.length < 3) && (search.address.length < 3) && (search.phone.length < 3) && (search.email.length < 3)) {
6270                        return;
6271                    }
6272
6273                    // abort any searches that haven't returned yet, so that we don't get a pile of results in random order
6274                    if (promise && promise.state() === 'pending') {
6275                        promise.abort();
6276                    }
6277
6278                    var searchParams = [];
6279                    if (search.name) {
6280                        searchParams.push("name=" + encodeURIComponent(search.name));
6281                    }
6282
6283                    // also search additional search fields if they are visible
6284                    if (search.address && $searchFieldAddress.is(':visible')) {
6285                        searchParams.push("address=" + encodeURIComponent(search.address));
6286                    }
6287
6288                    if (search.phone && $searchFieldPhone.is(':visible')) {
6289                        searchParams.push("phone=" + encodeURIComponent(search.phone));
6290                    }
6291
6292                    if (search.email && $searchFieldEmail.is(':visible')) {
6293                        searchParams.push("email=" + encodeURIComponent(search.email));
6294                    }
6295
6296                    var searchQuery = "?" + searchParams.join("&");
6297
6298                    // set the timeout to 20 seconds, just in case it takes a long time to search
6299                    promise = $.ajax({
6300                        url: restUrl
6301                            + searchQuery
6302                            + "&includeDetails=" + includeDetails
6303                            + "&includeBusinesses=" + includeBusinesses
6304                            + "&includeDeceased=" + includeDeceased,
6305                        timeout: 20000,
6306                        dataType: 'json'
6307                    });
6308
6309                    // Display a wait indicator to show that the search is now running.
6310                    if ($('.js-searching-notification').length == 0) {
6311                        $searchResults.prepend('<i class="ti ti-refresh ti-spin margin-l-md js-searching-notification" style="display: none; opacity: .4;"></i>');
6312                    }
6313                    $('.js-searching-notification').fadeIn(800);
6314
6315                    promise.done(function (data) {
6316                        $searchResults.html('');
6317                        response($.map(data, function (item) {
6318                            return item;
6319                        }));
6320
6321                        exports.personPickers[controlId].updateScrollbar();
6322                    });
6323
6324                    promise.fail(function (xhr, status, error) {
6325                        console.log(status + ' [' + error + ']: ' + xhr.responseText);
6326                        var errorCode = xhr.status;
6327                        if (errorCode == 401) {
6328                            $searchResults.html("<li class='text-danger'>Sorry, you're not authorized to search.</li>");
6329                        }
6330
6331                        $('.js-searching-notification').remove();
6332                    });
6333                },
6334                // Set minLength to 0, but check that at least one field has 3 chars before fetching from REST.
6335                // To minimize load on the server, don't trigger the search until a reasonable delay after the last keypress.
6336                minLength: 0,
6337                delay: 750,
6338                html: true,
6339                appendTo: $searchResults,
6340                pickerControlId: controlId,
6341                messages: {
6342                    noResults: function () { },
6343                    results: function () { }
6344                }
6345            });
6346
6347            var autoCompleteCustomRenderItem = function ($ul, item) {
6348                if (this.options.html) {
6349                    // override jQueryUI autocomplete's _renderItem so that we can do HTML for the ListItems
6350                    // derived from http://github.com/scottgonzalez/jquery-ui-extensions
6351
6352                    var inactiveWarning = "";
6353
6354                    if (!item.IsActive && item.RecordStatus) {
6355                        inactiveWarning = " <small>(" + item.RecordStatus + ")</small>";
6356                    }
6357                    if (item.IsDeceased) {
6358                        inactiveWarning = " <small class=\"text-danger\">(Deceased)</small>";
6359                    }
6360
6361                    var quickSummaryInfo = "";
6362                    if (item.FormattedAge || item.SpouseNickName) {
6363                        quickSummaryInfo = " <small class='rollover-item text-muted'>";
6364                        if (item.FormattedAge) {
6365                            quickSummaryInfo += "Age: " + item.FormattedAge;
6366                        }
6367
6368                        if (item.SpouseNickName) {
6369                            if (item.FormattedAge) {
6370                                quickSummaryInfo += "; ";
6371                            }
6372
6373                            quickSummaryInfo += "Spouse: " + item.SpouseNickName;
6374                        }
6375
6376                        quickSummaryInfo += "</small>";
6377                    }
6378
6379                    var $div = $('<div/>').attr('class', 'radio'),
6380
6381                        $label = $('<label/>')
6382                            .html('<span class="label-text">' + item.Name + inactiveWarning + quickSummaryInfo + '</span><i class="ti ti-refresh ti-spin margin-l-md loading-notification" style="display: none; opacity: .4;"></i>')
6383                            .prependTo($div),
6384
6385                        $radio = $('<input type="radio" name="person-id" />')
6386                            .attr('id', item.Id)
6387                            .attr('value', item.Id)
6388                            .prependTo($label),
6389
6390                        $li = $('<li/>')
6391                            .addClass('picker-select-item js-picker-select-item')
6392                            .attr('data-person-id', item.Id)
6393                            .attr('data-person-name', item.Name)
6394                            .html($div),
6395
6396                        $resultSection = $(this.options.appendTo);
6397
6398                    var $itemDetailsDiv = $('<div/>')
6399                        .addClass('picker-select-item-details js-picker-select-item-details clearfix');
6400
6401                    if (item.SearchDetailsHtml) {
6402                        $itemDetailsDiv.attr('data-has-details', true).html(item.SearchDetailsHtml);
6403                    }
6404                    else {
6405                        $itemDetailsDiv.attr('data-has-details', false);
6406                    }
6407
6408                    if (includeDetails === 'false') {
6409                        $itemDetailsDiv.hide();
6410                    }
6411
6412                    $itemDetailsDiv.appendTo($li);
6413
6414                    if (!item.IsActive) {
6415                        $li.addClass('is-inactive');
6416                    }
6417
6418                    return $resultSection.append($li);
6419                }
6420                else {
6421                    return $('<li></li>')
6422                        .data('item.autocomplete', item)
6423                        .append($('<a></a>').text(item.label))
6424                        .appendTo($ul);
6425                }
6426            }
6427
6428            // each search field has its own autocomplete object, so we'll need override with our custom _renderItem to each
6429            $.each(autoCompletes, function (a, b, c) {
6430                var autoComplete = $(autoCompletes[a]).data('ui-autocomplete');
6431
6432                // Debugging: override close to prevent it canceling when loosing focus when debugging
6433                // autoComplete.close = function () { };
6434
6435                autoComplete._renderItem = autoCompleteCustomRenderItem;
6436            });
6437
6438            $pickerToggle.on('click', function (e) {
6439                e.preventDefault();
6440                $(this).toggleClass("active");
6441                $pickerMenu.toggle(0, function () {
6442                    exports.personPickers[controlId].updateScrollbar();
6443                    $searchFieldName.trigger('focus');
6444                });
6445            });
6446
6447            $pickerControl.on('click', '.js-picker-select-item', function (e) {
6448                if (e.type == 'click' && $(e.target).is(':input') == false) {
6449                    // only process the click event if it has bubbled up to the input tag
6450                    return;
6451                }
6452
6453                e.stopPropagation();
6454
6455                var $selectedItem = $(this).closest('.js-picker-select-item');
6456                var $itemDetails = $selectedItem.find('.js-picker-select-item-details');
6457
6458                var selectedPersonId = $selectedItem.attr('data-person-id');
6459
6460                if ($itemDetails.is(':visible')) {
6461
6462                    if (selectedPersonId == lastSelectedPersonId && e.type == 'click') {
6463                        // 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
6464                        var selectedText = $selectedItem.attr('data-person-name');
6465
6466                        setSelectedPerson(selectedPersonId, selectedText);
6467
6468                        $pickerSelect.trigger('onclick');
6469                        // Fire the postBack for the Select button.
6470                        var postBackUrl = $pickerSelect.prop('href');
6471                        if (postBackUrl) {
6472                            window.location = postBackUrl;
6473                        }
6474                    } else {
6475
6476                        // if it is already visible but isn't the same one twice, just leave it open
6477                    }
6478                }
6479
6480                if (includeDetails === 'false') {
6481                    // hide other open details
6482                    $('.js-picker-select-item-details', $pickerControl).filter(':visible').each(function () {
6483                        var $el = $(this),
6484                            currentPersonId = $el.closest('.js-picker-select-item').attr('data-person-id');
6485
6486                        if (currentPersonId != selectedPersonId) {
6487                            $el.slideUp();
6488                            exports.personPickers[controlId].updateScrollbar();
6489                        }
6490                    });
6491                }
6492
6493                lastSelectedPersonId = selectedPersonId;
6494
6495                if ($itemDetails.attr('data-has-details') == 'false') {
6496                    // add a spinner in case we have to wait on the server for a little bit
6497                    var $spinner = $selectedItem.find('.loading-notification');
6498                    $spinner.fadeIn(800);
6499
6500                    // fetch the search details from the server
6501                    $.get(restDetailUrl + '?Id=' + selectedPersonId, function (responseText, textStatus, jqXHR) {
6502                        $itemDetails.attr('data-has-details', true);
6503
6504                        // hide then set the HTML so that we can get the slideDown effect
6505                        $itemDetails.stop().hide().html(responseText);
6506                        showItemDetails($itemDetails);
6507
6508                        $spinner.stop().fadeOut(200);
6509                    });
6510                } else {
6511                    showItemDetails($selectedItem.find('.picker-select-item-details:hidden'));
6512                }
6513            });
6514
6515            var showItemDetails = function ($itemDetails) {
6516                if ($itemDetails.length) {
6517                    $itemDetails.slideDown(function () {
6518                        exports.personPickers[controlId].updateScrollbar();
6519                    });
6520                }
6521            }
6522
6523            $pickerControl.on('mouseenter',
6524                function () {
6525                    // only show the X if there is something picked
6526                    if (($pickerPersonId.val() || '0') !== '0') {
6527                        $pickerSelectNone.addClass('show-hover');
6528                    }
6529                });
6530
6531            $pickerCancel.on('click', function () {
6532
6533                clearSearchFields();
6534                $pickerMenu.slideUp(function () {
6535                    exports.personPickers[controlId].updateScrollbar();
6536                });
6537            });
6538
6539            $pickerSelectNone.on('click', function (e) {
6540                // prevent the click from bubbling up to the pickerControl click event
6541                e.preventDefault();
6542                e.stopPropagation();
6543
6544                var selectedValue = '0',
6545                    selectedText = defaultText;
6546
6547                $pickerPersonId.val(selectedValue);
6548                $pickerPersonName.val(selectedText);
6549                // run onclick event from the button
6550                $(this).trigger('onclick');
6551            });
6552
6553            // disable the enter key : this will prevent the enter key from clearing the loaded search query.
6554            $pickerControl.on('keypress', function (e) {
6555                if (e.which == 13) {
6556                    return false;
6557                }
6558            });
6559
6560            var setSelectedPerson = function (selectedValue, selectedText) {
6561                var selectedPersonLabel = $pickerControl.find('.js-personpicker-selectedperson-label');
6562
6563                $pickerPersonId.val(selectedValue);
6564                $pickerPersonName.val(selectedText);
6565
6566                selectedPersonLabel.val(selectedValue);
6567                selectedPersonLabel.text(selectedText);
6568
6569                $pickerMenu.slideUp();
6570            }
6571
6572            var clearSearchFields = function () {
6573                $searchFieldName.val('');
6574                $searchFieldAddress.val('');
6575                $searchFieldPhone.val('');
6576                $searchFieldEmail.val('');
6577            }
6578
6579            $pickerSelect.on('click', function () {
6580                var $radInput = $pickerControl.find('input:checked'),
6581                    selectedValue = $radInput.val(),
6582                    selectedText = $radInput.closest('.js-picker-select-item').attr('data-person-name');
6583
6584                setSelectedPerson(selectedValue, selectedText);
6585                clearSearchFields();
6586            });
6587
6588            var toggleSearchFields = function () {
6589                var expanded = $pickerExpandSearchFields.val();
6590                if (expanded == 1) {
6591                    $pickerAdditionalSearchFields.slideDown();
6592                }
6593                else {
6594                    $pickerAdditionalSearchFields.slideUp();
6595                }
6596
6597                $pickerToggleAdditionalSearchFields.toggleClass('active', expanded == 1);
6598            };
6599
6600            toggleSearchFields();
6601
6602            $pickerToggleAdditionalSearchFields.on('click', function () {
6603                var expanded = $pickerExpandSearchFields.val();
6604                if (expanded == 1) {
6605                    expanded = 0;
6606                }
6607                else {
6608                    expanded = 1;
6609                }
6610
6611                $pickerExpandSearchFields.val(expanded);
6612
6613                toggleSearchFields();
6614            });
6615
6616            $('.js-select-self', $pickerControl).on('click', function () {
6617                var selectedValue = $('.js-self-person-id', $pickerControl).val(),
6618                    selectedText = $('.js-self-person-name', $pickerControl).val();
6619
6620                setSelectedPerson(selectedValue, selectedText);
6621
6622                // fire the postBack of the btnSelect if there is one
6623                $pickerSelect.trigger('onclick');
6624                var postBackUrl = $pickerSelect.prop('href');
6625                if (postBackUrl) {
6626                    window.location = postBackUrl;
6627                }
6628            });
6629        };
6630
6631        PersonPicker.prototype.updateScrollbar = function () {
6632            var self = this;
6633
6634            // first, update this control's scrollbar, then the modal'
6634s
6635
6636            if (self.$pickerScrollContainer.is(':visible')) {
6637                if (self.iScroll) {
6638                    self.iScroll.refresh();
6639                }
6640            }
6641
6642            // update the outer modal scrollbar
6643            Rock.dialogs.updateModalScrollBar(this.controlId);
6644        }
6645
6646        PersonPicker.prototype.initialize = function () {
6647
6648            this.iScroll = new IScroll($('.viewport', this.$pickerControl)[0], {
6649                mouseWheel: true,
6650                indicators: {
6651                    el: $('.track', this.$pickerScrollContainer)[0],
6652                    interactive: true,
6653                    resize: false,
6654                    listenY: true,
6655                    listenX: false,
6656                },
6657                click: false,
6658                preventDefaultException: { tagName: /.*/ }
6659            });
6660
6661            this.initializeEventHandlers();
6662        };
6663
6664        var exports = {
6665            personPickers: {},
6666            findControl: function (controlId) {
6667                return exports.personPickers[controlId];
6668            },
6669            initialize: function (options) {
6670                if (!options.controlId) throw '`controlId` is required.';
6671                if (!options.restUrl) throw '`restUrl` is required.';
6672
6673                var personPicker = new PersonPicker(options);
6674                exports.personPickers[options.controlId] = personPicker;
6675                personPicker.initialize();
6676            }
6677        };
6678        return exports;
6679    }());
6680}(jQuery));
6681;
6682(function($) {
6683    'use strict';
6684    window.Rock = window.Rock || {};
6685    Rock.controls = Rock.controls || {};
6686
6687    Rock.controls.priorityNav = (function() {
6688        var RESIZE_DURATION = 500;
6689        var TAB_KEYCODE = 9;
6690        var ClassName = {
6691            PRIORITY: 'priority',
6692            HIDE: 'sr-only',
6693            RESIZING: 'resizing'
6694        };
6695        var Selector = {
6696            NAV_ELEMENTS: "li:not('.overflow-nav')",
6697            FIRST_ELEMENT: 'li:first',
6698            PRIORITY_ELEMENT: '.priority'
6699        };
6700        var MenuLabelDefault = 'More';
6701        var MenuLabelAllHiddenDefault = 'Menu';
6702        var MenuLabelCarat = '<i class="ml-1 ti ti-chevron-down" />'
6703        var MenuTemplate = function(MenuLabel) {
6704            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>';
6705        };
6706        var PriorityNav = function (options) {
6707            var element = $('.nav-tabs');
6708
6709            if (options && options.controlId) {
6710                element = $('#' + options.controlId);
6711            } else {
6712                options = '';
6713            }
6714
6715            this._element = element;
6716            this._config = options;
6717
6718            if ($(element).is('ul')) {
6719                this._$menu = $(element);
6720            } else {
6721                this._$menu = $(element)
6722                    .find('ul')
6723                    .first();
6724            }
6725            this._initMenu();
6726            this._$allNavElements = this._$menu.find(Selector.NAV_ELEMENTS);
6727            this._bindUIActions();
6728            this._setupMenu();
6729        };
6730
6731        PriorityNav.prototype = {
6732            constructor: PriorityNav,
6733            initialize: function() {},
6734            _initMenu: function() {
6735                var MenuLabel = this._config.MenuLabel
6736
6737                if (typeof MenuLabel === 'undefined') {
6738                  MenuLabel = MenuLabelDefault
6739                }
6740
6741                // add menu template
6742                this._$menu.append(MenuTemplate(MenuLabel));
6743            },
6744
6745            _setupMenu: function() {
6746                var $allNavElements = this._$allNavElements;
6747                // Checking position of the menu
6748                var menuPosition = this._$menu.position();
6749                // Get position of right
6750                var menuRight = menuPosition.left + this._$menu.outerWidth();
6751                // Checking top position of first item (sometimes changes)
6752                var firstPos = this._$menu.find(Selector.FIRST_ELEMENT).position();
6753
6754                // Empty collection in which to put menu items to move
6755                var $wrappedElements = $();
6756
6757                // Used to snag the previous menu item in addition to ones that have wrapped
6758                var first = true;
6759
6760                // Loop through all the nav items...
6761                this._$allNavElements.each(function(i) {
6762                    var $elm = $(this);
6763
6764                    // ...in which to find wrapped elements
6765                    var pos = $elm.position();
6766                    // Get position of right
6767                    var right = pos.left + $elm.outerWidth();
6768
6769                    if (pos.top !== firstPos.top || right > menuRight) {
6770                        // If element is wrapped, add it to set
6771                        $wrappedElements = $wrappedElements.add($elm);
6772
6773                        // Add the previous one too, if first
6774                        if (first) {
6775                            $wrappedElements = $wrappedElements.add($allNavElements.eq(i - 1));
6776                            first = false;
6777                        }
6778                    }
6779                });
6780
6781                if ($wrappedElements.length) {
6782                    // Clone set before altering
6783                    var newSet = $wrappedElements.clone();
6784
6785                    // Hide ones that we're moving
6786                    $wrappedElements.addClass(ClassName.HIDE);
6787                    $wrappedElements.find('a').attr('tabindex', -1);
6788
6789                    // Add wrapped elements to dropdown
6790                    this._$menu.find('.overflow-nav-list').append(newSet);
6791
6792                    // Show new menu
6793                    this._$menu.find('.overflow-nav').removeClass('d-none');
6794
6795                    // Check if menu doesn't overflow after process
6796                    if (this._$menu.find('.overflow-nav').position().top !== firstPos.top) {
6797                        var $item = $(this._element)
6798                            .find('.' + ClassName.HIDE)
6799                            .first()
6800                            .prev();
6801                        var $itemDuplicate = $item.clone();
6802
6803                        $item.addClass(ClassName.HIDE);
6804                        $item.find('a').attr('tabindex', -1);
6805
6806                        this._$menu.find('.overflow-nav-list').prepend($itemDuplicate);
6807                    }
6808
6809                    if ($allNavElements.length == $wrappedElements.length) {
6810                        this._$menu.find('.overflow-nav-link').html(MenuLabelAllHiddenDefault + ' ' + MenuLabelCarat);
6811                        this._$menu.find('.overflow-nav-list').removeClass('dropdown-menu-right');
6812                    } else {
6813                        this._$menu.find('.overflow-nav-link').html(MenuLabelDefault + ' ' + MenuLabelCarat);
6814                        this._$menu.find('.overflow-nav-list').addClass('dropdown-menu-right');
6815                    }
6816                }
6817
6818                // hide menu from AT
6819                this._$menu.find('.overflow-nav').attr('aria-hidden', true);
6820            },
6821
6822            _tearDown: function() {
6823                this._$menu.find('.overflow-nav-list').empty();
6824                this._$menu.find('.overflow-nav').addClass('d-none');
6825                this._$allNavElements.removeClass(ClassName.HIDE);
6826                this._$allNavElements.find('a').attr('tabindex', 0);
6827            },
6828
6829            _bindUIActions: function() {
6830                var self = this;
6831                $(window).on('resize', function() {
6832                    self._$menu.addClass(ClassName.RESIZING);
6833
6834                    setTimeout( function() {
6835                        self._tearDown();
6836                        self._setupMenu();
6837                        self._$menu.removeClass(ClassName.RESIZING);
6838                    }, RESIZE_DURATION);
6839                });
6840
6841                this._$menu.find('.overflow-nav .dropdown-toggle').on('keyup', function(e) {
6842                    if (e.which === TAB_KEYCODE) {
6843                        $(e.target).dropdown('toggle');
6844                    }
6845                });
6846            }
6847        }
6848
6849        var exports = {
6850            defaults: {
6851                controlId: null,
6852                name: 'prioritynav'
6853            },
6854            controls: {},
6855            initialize: function (options) {
6856                var settings = $.extend({}, exports.defaults, options);
6857
6858                if (!settings.controlId) throw 'controlId is required';
6859
6860                var priorityNav = new PriorityNav(settings);
6861
6862                // Delay initialization until after the DOM is ready
6863                $(function () {
6864                    priorityNav.initialize();
6865                });
6866            }
6867        };
6868
6869        return exports;
6870    })();
6871})(jQuery);
6872;
6873(function ($) {
6874    'use strict';
6875    window.Rock = window.Rock || {};
6876    Rock.controls = Rock.controls || {};
6877
6878    Rock.controls.rangeSlider = (function () {
6879        var exports = {
6880            initialize: function (options) {
6881                if (!options.controlId) {
6882                    throw 'controlId is required';
6883                }
6884
6885                Sys.Application.add_load(function () {
6886                    var cssFile = Rock.settings.get('baseUrl') + 'Scripts/ion.rangeSlider/css/ion.rangeSlider.Rock.css';
6887
6888                    // ensure that css for rangeSlider is added to page
6889                    if (!$('#rangeSliderCss').length) {
6890                        $('head').append("<link id='rangeSliderCss' href='" + cssFile + "' type='text/css' rel='stylesheet' />");
6891                    }
6892                });
6893
6894                // use https://github.com/IonDen/ion.rangeSlider/ to make a slider
6895                // see https://github.com/IonDen/ion.rangeSlider/#settings for settings
6896                $('#' + options.controlId).ionRangeSlider({
6897                    type: options.type || 'single', // Choose slider t
6897ype, could be 'single' for one handle, or 'double' for two handles
6898                    min: options.min || 0,
6899                    max: options.max || 100,
6900                    step: options.step || 1,
6901                    from: options.from || null, // for 'single' the position of the slider. for 'double' the lower position of the selected range
6902                    to: options.to || null, // if 'double' the upper position of the selected range
6903                    disable: options.disable || false // if 'true', the control will render as read-only
6904                });
6905
6906            }
6907        };
6908
6909        return exports;
6910    }());
6911}(jQuery));
6912;
6913(function ($) {
6914    'use strict';
6915    window.Rock = window.Rock || {};
6916    Rock.controls = Rock.controls || {};
6917
6918    Rock.controls.rockCheckBoxList = (function () {
6919
6920        var exports = {
6921            clientValidate: function (validator, args) {
6922                var $checkBoxList = $(validator).closest('.js-rockcheckboxlist');
6923
6924                var checkboxes = $checkBoxList.find('input');
6925                var isValid = false;
6926                for (var i = 0; i < checkboxes.length; i++) {
6927                    if (checkboxes[i].checked) {
6928                        isValid = true;
6929                        break;
6930                    }
6931                }
6932
6933                if (isValid) {
6934                        $checkBoxList.removeClass('has-error');
6935                } else {
6936                        $checkBoxList.addClass('has-error');
6937                }
6938
6939                args.IsValid = isValid;
6940            }
6941        };
6942
6943        return exports;
6944    }());
6945}(jQuery));
6946
6947
6948;
6949(function (Sys) {
6950    'use strict';
6951    Sys.Application.add_load(function () {
6952
6953        var $chosenDropDowns = $('.chosen-select');
6954        if ($chosenDropDowns.length) {
6955            // IE fix for 'chosen' causing activeElement to be a plain generic object (not a real node) after postback
6956            if (document.activeElement && !document.activeElement.nodeType) {
6957                $('body').trigger('focus');
6958            }
6959
6960            $chosenDropDowns.chosen({
6961                width: '100%',
6962                allow_single_deselect: true,
6963                placeholder_text_multiple: ' ',
6964                placeholder_text_single: ' '
6965            });
6966
6967            $chosenDropDowns.on('chosen:showing_dropdown chosen:hiding_dropdown', function (evt, params) {
6968                // update the outer modal
6969                Rock.dialogs.updateModalScrollBar(this);
6970            });
6971
6972            var $chosenDropDownsAbsolute = $chosenDropDowns.filter('.chosen-select-absolute');
6973            if ($chosenDropDownsAbsolute.length) {
6974                $chosenDropDownsAbsolute.on('chosen:showing_dropdown', function (evt, params) {
6975                    $(this).next('.chosen-container').find('.chosen-drop').css('position', 'relative');
6976                });
6977                $chosenDropDownsAbsolute.on('chosen:hiding_dropdown', function (evt, params) {
6978                    $(this).next('.chosen-container').find('.chosen-drop').css('position', 'absolute');
6979                });
6980            }
6981        }
6982    });
6983}(Sys));
6984;
6985(function ($, Sys) {
6986    'use strict';
6987    window.Rock = window.Rock || {};
6988    Rock.controls = Rock.controls || {};
6989
6990    Rock.controls.scheduleBuilder = (function () {
6991        var exports,
6992            ScheduleBuilder = function (options) {
6993                this.id = options.id;
6994            };
6995
6996        ScheduleBuilder.prototype.initializeEventHandlers = function () {
6997
6998            var id = this.id,
6999                $modal = $('#' + id).closest('.rock-modal');
7000
7001
7002            $modal.find('.schedule-type').off('click').on('click',function () {
7003                var recurrenceState = $('input[class=schedule-type]:checked').data('schedule-type');
7004
7005                if (recurrenceState === 'schedule-onetime') {
7006                    $modal.find('.js-schedule-recurrence-panel').slideUp();
7007                } else {
7008                    $modal.find('.js-schedule-recurrence-panel').slideDown();
7009                }
7010            });
7011
7012            $modal.find('.recurrence-pattern-radio').off('click').on('click', function () {
7013
7014                var recurrencePattern = '.' + $('input[class=recurrence-pattern-radio]:checked').data('recurrence-pattern');
7015
7016                if ($modal.find(recurrencePattern)) {
7017
7018                    $modal.find('.recurrence-pattern-type').not(recurrencePattern).hide();
7019
7020                    if (recurrencePattern == ".recurrence-pattern-specific-date") {
7021                        /* don't show continue-until or exclusion dates for specific date mode (since it doesn't make sense, plus ical ends up deleting them) */
7022                        $modal.find('.js-continue-until').hide();
7023                        $modal.find('.js-exclusion-dates').hide();
7024                    }
7025                    else {
7026                        $modal.find('.js-continue-until').show();
7027                        $modal.find('.js-exclusion-dates').show();
7028                    }
7029
7030                    $(recurrencePattern).fadeIn();
7031                }
7032            });
7033
7034            /** Specific Dates Scripts**/
7035
7036            // show datepicker, ok, cancel so that new date can be added to the list
7037            $modal.find('.add-specific-date').off('click').on('click', function () {
7038                $(this).hide();
7039                $modal.find('.js-add-specific-date-group').show();
7040            });
7041
7042            // add new date to list when ok is clicked
7043            $modal.find('.add-specific-date-ok').off('click').on('click', function () {
7044
7045                // get date list from hidden field
7046                var dateListValues = $modal.find('.recurrence-pattern-type > input:hidden').val().split(',');
7047
7048                if (!dateListValues[0]) {
7049                    // if dateList is blank, initialize as a new empty array
7050                    dateListValues = [];
7051                }
7052
7053                // validate
7054
7055                // set colors back to default just in case previously marked invalid
7056                var $datepicker = $modal.find('.specific-date input');
7057                $datepicker.css('color', '');
7058
7059                // var checkDate = Date.parse($datepicker.val());
7060                if ($datepicker.val() == null || $datepicker.val() == "") {
7061                    // blank, don't color, just return
7062                    return;
7063                }
7064
7065                var locale = window.navigator.userLanguage || window.navigator.language;
7066                moment.locale(locale);
7067                var checkDate = moment($datepicker.val(), 'l');
7068
7069                if (!checkDate.isValid) {
7070                    // invalid date entered, color red and return
7071                    $datepicker.css('color', 'red');
7072                    return;
7073                }
7074
7075                var newDate = $datepicker.val();
7076
7077                // delete newDate from list in case it is already there
7078                var index = dateListValues.indexOf(newDate);
7079                if (index >= 0) {
7080                    dateListValues.splice(index, 1);
7081                }
7082
7083                // add new date to list
7084                dateListValues.push(newDate);
7085
7086                // save list back to hidden field
7087                $modal.find('.js-specific-datelist-values').val(dateListValues);
7088
7089                // rebuild the UL
7090                var dateList = $modal.find(".lstSpecificDates");
7091                dateList.children().remove();
7092                $.each(dateListValues, function (i, value) {
7093                    // add to ul
7094                    var newLi = "<li><span>" + value + "</span> <a href='#' style='display: none'> <i class='ti ti-x'></i></a></li>";
7095                    dateList.append(newLi);
7096                });
7097
7098                $modal.find('.js-add-specific-date-group').hide();
7099                $modal.find('.add-specific-date').show();
7100            });
7101
7102            // cancel out of adding a new date
7103            $modal.find('.add-specific-date-cancel').off('click').on('click', function () {
7104                $modal.find('.js-add-specific-date-group').hide();
7105                $modal.find('.add-specific-date').show();
7106            });
7107
7108            // fadeIn/fadeOut the X buttons to delete dates
7109            $modal.find('.lstSpecificDates').on('mouseenter',
7110                function () {
7111                    $(this).find('li a').stop(true, true).show();
7112                }).on('mouseleave',
7113                function () {
7114                    $(this).find('li a').stop(true, true).fadeOut(500);
7115                });
7116
7117            // delete specific date from list
7118            $modal.find('.lstSpecificDates').off('click').on('click', 'li a', function () {
7119                var selectedDate = $(this).siblings().text();
7120
7121                // get date list from hidden field
7122                var $hiddenField = $modal.find('.js-specific-datelist-values');
7123                var dateList = $hiddenField.val().split(',');
7124
7125                // delete selectedDate
7126                var index = dateList.indexOf(selectedDate);
7127                if (index >= 0) {
7128                    dateList.splice(index, 1);
7129                }
7130
7131                // save list back to hidden field
7132                $hiddenField.val(dateList);
7133
7134                // remove date from ul list
7135                var liItem = $(this).parent();
7136                liItem.remove();
7137            });
7138
7139            /** Exclusion DateRanges Scripts **/
7140
7141            // show dateRangepicker, ok, cancel so that new dateRange can be added to the list
7142            $modal.find('.add-exclusion-daterange').off('click').on('click', function () {
7143
7144                $(this).hide();
7145                $modal.find('.js-add-exclusion-daterange-group').show(function () {
7146                    Rock.controls.modal.updateSize();
7147                });
7148            });
7149
7150            // add new date to list when ok is clicked
7151            $modal.find('.add-exclusion-daterange-ok').off('click').on('click', function () {
7152
7153                // get daterange list from hidden field
7154                var dateRangeListValues = $modal.find('.js-exclusion-daterange-list-values').val().split(',');
7155                if (!dateRangeListValues[0]) {
7156                    // if blank, initialize as a new empty array
7157                    dateRangeListValues = [];
7158                }
7159
7160                var $exclusionDateRange = $modal.find('.js-exclusion-date-range-picker input');
7161                var newDateRange = $exclusionDateRange.first().val() + ' - ' + $exclusionDateRange.last().val();
7162
7163                // delete newDateRange from list in case it is already there
7164                var index = dateRangeListValues.indexOf(newDateRange);
7165                if (index >= 0) {
7166                    dateRangeListValues.splice(index, 1);
7167                }
7168
7169                // add new dateRange to list
7170                dateRangeListValues.push(newDateRange);
7171
7172                // save list back to hidden field
7173                $modal.find('.js-exclusion-daterange-list-values').val(dateRangeListValues);
7174
7175                // rebuild the UL
7176                var dateRangeList = $modal.find('.lstExclusionDateRanges');
7177                dateRangeList.children().remove();
7178                $.each(dateRangeListValues, function (i, value) {
7179                    // add to ul
7180                    var newLi = "<li><span>" + value + "</span><a href='#' style='display: none'><i class='ti ti-x'></i></a></li>";
7181                    dateRangeList.append(newLi);
7182                });
7183
7184                $modal.find('.js-add-exclusion-daterange-group').hide();
7185                $modal.find('.add-exclusion-daterange').show();
7186            });
7187
7188            // cancel out of adding a new dateRange
7189            $modal.find('.add-exclusion-daterange-cancel').off('click').on('click', function () {
7190                $modal.find('.js-add-exclusion-daterange-group').hide();
7191                $modal.find('.add-exclusion-daterange').show();
7192            });
7193
7194            // fadeIn/fadeOut the X buttons to delete dateRanges
7195            $modal.find('.lstExclusionDateRanges').on('mouseenter',
7196                function () {
7197                    $(this).find('li a').stop(true, true).show();
7198                }).on('mouseleave',
7199                function () {
7200                    $(this).find('li a').stop(true, true).fadeOut(500);
7201                });
7202
7203            // delete dateRange from list
7204            $modal.find('.lstExclusionDateRanges').off('click').on('click', 'li a', function () {
7205                var selectedDateRange = $(this).siblings('span').text();
7206
7207                // get dateRange list from hidden field
7208                var $hiddenField = $modal.find('.js-exclusion-daterange-list-values');
7209                var dateRangeList = $hiddenField.val().split(',');
7210
7211                // delete selectedDateRange
7212                var index = dateRangeList.indexOf(selectedDateRange);
7213                if (index >= 0) {
7214                    dateRangeList.splice(index, 1);
7215                }
7216
7217                // save list back to hidden field
7218                $hiddenField.val(dateRangeList);
7219
7220                // remove dateRange from ul list
7221                var liItem = $(this).parent();
7222                liItem.remove();
7223            });
7224
7225            // validate on Save.  Make sure they have at least a StartDate and Time set
7226            $modal.find('.js-modaldialog-save-link').off('click').on('click', function (event) {
7227
7228                var locale = window.navigator.userLanguage || window.navigator.language;
7229                moment.locale(locale);
7230
7231                var $datetimepicker = $modal.find('[id*="dpStartDateTime"]').find('input'),
7232                    startDateValue = moment($datetimepicker.first().val(), 'l'),
7233                    startTimeValue = $datetimepicker.last().val();
7234
7235                if (!startDateValue.isValid) {
7236                    $datetimepicker.parents('.form-group').first().toggleClass('has-error', 1);
7237                    event.preventDefault();
7238                    return;
7239                }
7240                else {
7241                    $datetimepicker.parents('.form-group').first().toggleClass('has-error', 0);
7242                }
7243            });
7244        };
7245
7246        exports = {
7247            cache: {},
7248            initialize: function (options) {
7249                if (!options.id) {
7250                    throw 'id is required';
7251                }
7252
7253                var sb = new ScheduleBuilder(options);
7254                exports.cache[options.id] = sb;
7255
7256                Sys.Application.add_load(function () {
7257                    sb.initializeEventHandlers();
7258                });
7259            }
7260        };
7261
7262        return exports;
7263    }());
7264}(jQuery, Sys));
7265;
7266(function ($) {
7267    'use strict';
7268    window.Rock = window.Rock || {};
7269    Rock.controls = Rock.controls || {};
7270
7271    Rock.controls.screenKeyboard = (function () {
7272        //
7273        // Handle toggling the Shift key states.
7274        //
7275        var toggleShift = function ($keyboard) {
7276            $keyboard.find('[data-command="shift"]').toggleClass('active');
7277
7278            setAltMode($keyboard, $keyboard.find('[data-command="shift"].active').length > 0);
7279        };
7280
7281        //
7282        // Handle setting digits to either normal value or alternate value.
7283        //
7284        var setAltMode = function ($keyboard, altMode) {
7285            $keyboard.find('.digit').each(function () {
7286                $(this).text(altMode === true ? $(this).data('alt-value') : $(this).data('value'));
7287            });
7288        };
7289
7290        //
7291        // Process normal digit presses.
7292        //
7293        var digitClick = function () {
7294            var $keyboard = $(this).closest('.screen-keyboard');
7295            var $target = $('#' + $keyboard.data('target'));
7296
7297            $target.val($target.val() + $(this).text());
7298            $target.trigger('input');
7299
7300            if ($keyboard.find('[data-command="shift"].active').length > 0) {
7301                toggleShift($keyboard);
7302            }
7303        };
7304
7305        //
7306        // Process special command keys.
7307        //
7308        var commandClick = function (options) {
7309            var $keyboard = $(this).closest('.screen-keyboard');
7310            var $target = $('#' + $keyboard.data('target'));
7311            var command = $(this).data('command');
7312
7313            if (command === 'backspace') {
7314                var val = $target.val();
7315                if (val.length > 0) {
7316                    val = val.substr(0, val.length - 1);
7317                }
7318                $target.val(val);
7319                $target.trigger('input');
7320            }
7321            else if (command === 'clear') {
7322                $target.val('');
7323                $target.trigger('input');
7324            }
7325            else if (command === 'shift') {
7326                $keyboard.find('[data-command="caps"]').removeClass('active');
7327                toggleShift($keyboard);
7328            }
7329            else if (command === 'caps') {
7330                $keyboard.find('[data-command="shift"]').removeClass('active');
7331                $(this).toggleClass('active');
7332
7333                setAltMode($keyboard, $(this).hasClass('active'));
7334            }
7335            else if (command === 'tab') {
7336                /* TODO: What do we do with this action? */
7337            }
7338            else if (command === 'enter') {
7339                window.location = "javascript:__doPostBack('" + options.postback + "', 'enter')";
7340            }
7341        };
7342
7343        var exports = {
7344            initialize: function (options) {
7345                if (!options.id) {
7346                    throw 'id is required';
7347                }
7348
7349                var $keyboard = $('#' + options.id);
7350
7351                $keyboard.find('.digit').on('click', digitClick);
7352                $keyboard.find('.command').on('click', function () { commandClick.apply(this, [options]); });
7353            }
7354        };
7355
7356        return exports;
7357    }());
7358}(jQuery));
7359;
7360(function () {
7361    'use strict';
7362    window.Rock = window.Rock || {};
7363    Rock.controls = Rock.controls || {};
7364
7365    Rock.controls.searchField = (function () {
7366        var exports,
7367            SearchField = function (options) {
7368                this.controlId = options.controlId;
7369                this.$el = $('#' + this.controlId);
7370                this.name = options.name;
7371            };
7372
7373        SearchField.prototype = {
7374            constructor: SearchField,
7375            initialize: function () {
7376                var self = this;
7377
7378                var key = sessionStorage.getItem("com.rockrms.search");
7379                if (key && key != '') {
7380                    var $search = self.$el.parents('.smartsearch');
7381                    $search.find('input:hidden').val(key);
7382                    $search.find('a.dropdown-toggle > span').html($search.find('li[data-key="' + key + '"] > a').html());
7383                }
7384
7385                this.$el.typeahead({
7386                    name: this.name,
7387                    limit: 15,
7388                    remote: {
7389                        url: Rock.settings.get('baseUrl') + 'api/search?type=%TYPE&term=%QUERY&$top=15',
7390                        replace: function (url, uriEncodedQuery) {
7391                            var query = url;
7392                            query = query.replace('%TYPE', self.$el.parents('.smartsearch').find('input:hidden').val());
7393                            query = query.replace('%QUERY', uriEncodedQuery);
7394                            return query;
7395                        }
7396                    }
7397                });
7398
7399                this.initializeEventHandlers();
7400            },
7401            initializeEventHandlers: function () {
7402                var self = this,
7403                    search = function (term) {
7404
7405                        // clear the selected item from the input as it might have HTML which w
7405ould show while the search page loads
7406                        var searchControl = document.getElementById(self.controlId);
7407                        searchControl.value = '';
7408
7409                        // search for data elements in the search term
7410                        var $dataEl = $("<p>" + term + "</p>").find("data").first();
7411
7412                        // see if this is a universal search by looking for return-type and return-id data params
7413                        var returnType = $dataEl.attr("return-type");
7414                        var returnId = $dataEl.attr("return-id");
7415
7416                        if (returnType == null || returnId == null) {
7417                            var otherParams = '';
7418
7419                            // take any data attributes and add them as query parameters to the url
7420                            $dataEl.each(function () {
7421                                $.each(this.attributes, function () {
7422                                    if (this) {
7423                                        otherParams += '&' + this.name + '=' + encodeURIComponent(this.value);
7424                                    }
7425                                });
7426                            });
7427
7428                            // remove any search accessories. These are html elements with the class .search-accessory
7429                            var resultElement = document.createElement('div');
7430                            resultElement.innerHTML = term;
7431                            //var resultElement = $("<div/>").html(term);
7432                            var accessories = resultElement.getElementsByClassName('search-accessory');
7433
7434                            // remove all accessory elements
7435                            while (accessories[0]) { 
7436                                accessories[0].parentNode.removeChild(accessories[0]);
7437                            }
7438
7439                            // remove any html from the search term before putting it in the url
7440                            var targetTerm = resultElement.textContent.trim();
7441
7442                            var keyVal = self.$el.parents('.smartsearch').find('input:hidden').val(),
7443                                $li = self.$el.parents('.smartsearch').find('li[data-key="' + keyVal + '"]'),
7444                                targetUrl = $li.attr('data-target'),
7445                                url = Rock.settings.get('baseUrl') + targetUrl.replace('{0}', encodeURIComponent(targetTerm));
7446
7447                            if (otherParams != '') {
7448                                if (url.indexOf('?') > -1) {
7449                                    url += otherParams
7450                                } else {
7451                                    url += otherParams.replace(/^&/, '?')
7452                                }
7453                            }
7454
7455                            window.location = url;
7456                        } else {
7457                            // universal search uses returnType and returnId, so build the url for that
7458                            var keyVal = self.$el.parents('.smartsearch').find('input:hidden').val(),
7459                                $li = self.$el.parents('.smartsearch').find('li[data-key="' + keyVal + '"]'),
7460                                targetUrl = $li.attr('data-target'),
7461                                url = Rock.settings.get('baseUrl') + targetUrl.replace('{0}', encodeURIComponent(returnType) + "/" + encodeURIComponent(returnId));
7462
7463                            window.location = url;
7464                        }
7465                    };
7466
7467                // Listen for typeahead's custom events and trigger search when hit
7468                this.$el.on('typeahead:selected typeahead:autocompleted', function (e, obj, name) {
7469                    search(obj.value);
7470                });
7471
7472                // Listen for the ENTER key being pressed while in the search box and trigger search when hit
7473                this.$el.on('keydown', function (e) {
7474                    if (e.keyCode === 13) {
7475                        e.preventDefault();
7476                        return false;
7477                    }
7478                });
7479                this.$el.on('keyup', function (e) {
7480                    if (e.keyCode === 13 && "" !== $(this).val().trim() ) {
7481                        search($(this).val());
7482                    }
7483                });
7484
7485                // Wire up "change" handler for search type "dropdown menu"
7486                this.$el.parents('.smartsearch').find('.dropdown-menu a').on('click', function () {
7487                    var $this = $(this),
7488                        text = $this.html();
7489
7490                    var key = $this.parent().attr('data-key');
7491                    sessionStorage.setItem("com.rockrms.search", key);
7492
7493                    $this.parents('.dropdown-menu').siblings('.navbar-link').find('span').html(text);
7494                    self.$el.parents('.smartsearch').find('input:hidden').val(key)
7495                });
7496            }
7497        };
7498
7499        exports = {
7500            defaults: {
7501                controlId: null,
7502                name: 'search'
7503            },
7504            controls: {},
7505            initialize: function (options) {
7506                var searchField,
7507                    settings = $.extend({}, exports.defaults, options);
7508
7509                if (!settings.controlId) throw 'controlId is required';
7510
7511                if (!exports.controls[settings.controlId]) {
7512                    searchField = new SearchField(settings);
7513                    exports.controls[settings.controlId] = searchField;
7514                } else {
7515                    searchField = exports.controls[settings.controlId];
7516                }
7517
7518                // Delay initialization until after the DOM is ready
7519                $(function () {
7520                    searchField.initialize();
7521                });
7522            }
7523        };
7524
7525        return exports;
7526    }());
7527}());
7528;
7529(function ($) {
7530    'use strict';
7531    window.Rock = window.Rock || {};
7532    Rock.controls = Rock.controls || {};
7533
7534    Rock.controls.slidingDateRangePicker = (function () {
7535        var exports = {
7536            initialize: function (options) {
7537                if (!options.id) {
7538                    throw 'id is required';
7539                }
7540
7541                var $picker = $('#' + options.id);
7542
7543                // the dropdown with current, last, previous, daterange in it
7544                var $select = $picker.find('.js-slidingdaterange-select');
7545
7546                showHideControls($picker, $select);
7547                updateDateRangeInfo($picker);
7548
7549                $select.on('change', function () {
7550                    showHideControls($picker, $select);
7551                    updateDateRangeInfo($picker);
7552                });
7553
7554                $('.js-number, .js-time-units-singular, .js-time-units-plural, .js-slidingdaterange-select, .js-lower, .js-upper', $picker).on('change', function () {
7555                    updateDateRangeInfo($picker);
7556                });
7557
7558                $('.js-number', $picker).on('keyup', function () {
7559                    updateDateRangeInfo($picker);
7560                });
7561            }
7562        },
7563        showHideControls = function ($picker, $select) {
7564            var selectedValue = $select.val();
7565            var isLast = selectedValue == '0';
7566            var isCurrent = selectedValue == '1';
7567            var isDateRange = selectedValue == '2';
7568            var isPrevious = selectedValue == '4';
7569            var isNext = selectedValue == '8';
7570            var isUpcoming = selectedValue == '16';
7571            $picker.find('.js-number').toggle(isLast || isPrevious || isNext || isUpcoming);
7572            $picker.find('.js-time-units-singular').toggle(isCurrent);
7573            $picker.find('.js-time-units-plural').toggle(isLast || isPrevious || isNext || isUpcoming);
7574            $picker.find('.js-time-units-date-range').toggle(isDateRange);
7575            var $pickerContainer = $picker.closest('.js-slidingdaterange-container');
7576            if (isLast || isPrevious || isNext || isUpcoming || isCurrent) {
7577                $pickerContainer.find('.js-slidingdaterange-info').css("display", "inline");
7578            } else {
7579                $pickerContainer.find('.js-slidingdaterange-info').hide();
7580            }
7581        },
7582        updateDateRangeInfo = function ($picker) {
7583            var timeUnitType = 0;
7584            if ($picker.find('.js-time-units-singular').is(':visible')) {
7585                timeUnitType = $picker.find('.js-time-units-singular').val();
7586            } else {
7587                timeUnitType = $picker.find('.js-time-units-plural').val();
7588            }
7589
7590            var numberOf = $picker.find('.js-number').val();
7591
7592            var $pickerContainer = $picker.closest('.js-slidingdaterange-container');
7593
7594            var dateRangeString = '';
7595            var $select = $picker.find('.js-slidingdaterange-select');
7596            var selectedValue = $select.val();
7597            if (selectedValue == '2') {
7598                var startPicker = $pickerContainer.find('.js-lower');
7599                var startDate = startPicker.find('.form-control').val();
7600
7601                var endPicker = $pickerContainer.find('.js-upper');
7602                var endDate = endPicker.find('.form-control').val();
7603
7604                if (startDate != '' && endDate != '') {
7605                    dateRangeString = '&startDate=' + startDate + '&endDate=' + endDate;
7606                }
7607            }
7608
7609            if (selectedValue != '2' || dateRangeString != '') {
7610                var getDateRangeUrl = Rock.settings.get('baseUrl') + 'api/Utility/CalculateSlidingDateRange?slidingDateRangeType=' + $select.val() + '&timeUnitType=' + timeUnitType + '&number=' + numberOf + dateRangeString;
7611                $.get(getDateRangeUrl, function (r) {
7612                    $pickerContainer.find('.js-slidingdaterange-info').text(r);
7613                });
7614
7615                var getTextValueUrl = Rock.settings.get('baseUrl') + 'api/Utility/GetSlidingDateRangeTextValue?slidingDateRangeType=' + $select.val() + '&timeUnitType=' + timeUnitType + '&number=' + numberOf + dateRangeString;
7616                $.get(getTextValueUrl, function (r) {
7617                    $pickerContainer.find('.js-slidingdaterange-text-value').val(r).change();
7618                });
7619            }
7620        };
7621
7622        return exports;
7623    }());
7624}(jQuery));
7625;
7626(function ($) {
7627    'use strict';
7628    window.Rock = window.Rock || {};
7629    Rock.controls = Rock.controls || {};
7630
7631    Rock.controls.tagList = (function () {
7632        var TagList = function (options) {
7633                this.controlId = options.controlId;
7634                this.entityTypeId = options.entityTypeId;
7635                this.currentPersonId = options.currentPersonId;
7636                this.entityGuid = options.entityGuid;
7637                this.entityQualifierColumn = options.entityQualifierColumn;
7638                this.entityQualifierValue = options.entityQualifierValue;
7639                this.preventTagCreation = options.preventTagCreation;
7640                this.delaySave = options.delaySave;
7641                this.categoryGuid = options.categoryGuid;
7642                this.includeInactive = options.includeInactive;
7643            },
7644            exports;
7645
7646        TagList.prototype.verifyTag = function (tagName) {
7647            // Since `verifyTag` is being called by the jQuery lib, `this`
7648            // is the current TagList DOM element. Get the HTML ID and fetch
7649            // from the cache.
7650            var tagList = exports.tagLists[$(this).attr('id')];
7651            if (! /^((?!<)(?!>)(?!%)(?!&).)*$/.test(tagName)) {
7652                Rock.dialogs.alert("Invalid characters have been entered for the tag name. Angle brackets, percent, and ampersand are not allowed.");
7653                $('#' + tagList.controlId).removeTag(tagName);
7654                return;
7655            }
7656
7657            var restUrl = Rock.settings.get('baseUrl') + 'api/tags';
7658            restUrl += '?entityTypeId=' + tagList.entityTypeId;
7659            restUrl += '&ownerId=' + tagList.currentPersonId;
7660            restUrl += '&name=' + encodeURIComponent(tagName);
7661            restUrl += '&includeInactive' + tagList.includeInactive.toString();
7662
7663            if (tagList.entityQualifierColumn) {
7664                restUrl += '&entityQualifier=' + tagList.entityQualifierColumn;
7665            }
7666
7667            if (tagList.entityQualifierValue) {
7668                restUrl += '&entityQualifierValue=' + tagList.entityQualifierValue;
7669            }
7670
7671            if (tagList.categoryGuid && tagList.categoryGuid != '') {
7672                restUrl += '&categoryGuid=' + tagList.categoryGuid;
7673            }
7674
7675            $.ajax({
7676                url: restUrl,
7677                statusCode: {
7678                    404: function () {
7679                        if (tagList.preventTagCreation) {
7680                            $('#' + tagList.controlId).removeTag(tagName);
7681                        }
7682                        else {
7683                            Rock.dialogs.confirm('A tag called "' + $('<div/>').text(tagName).html() + '" does not exist. Do you want to create a new personal tag?', function (result) {
7684                                if (result) {
7685                                    tagList.addTag(tagName);
7686                                } else {
7687                                    $('#' + tagList.controlId).removeTag(tagName);
7688                                }
7689                            });
7690                        }
7691                    },
7692                    403: function () {
7693                        Rock.dialogs.alert("Invalid characters have been entered for the tag name. Angle brackets, percent, and ampersand are not allowed.");
7694                        $('#' + tagList.controlId).removeTag(tagName);
7695                    },
7696                    200: function () {
7697                        tagList.addTag(tagName);
7698                    }
7699                }
7700            });
7701        };
7702
7703        TagList.prototype.addTag = function (tagName) {
7704            // `addTag` is invoked by `verifyTag` on an instance of a `TagList` object.
7705            // `this` is the current TagList instance in scope.
7706
7707            var tagList = this,
7708                restUrl = Rock.settings.get('baseUrl') + 'api/taggeditems'
7709            restUrl += '?entityTypeId=' + tagList.entityTypeId;
7710            restUrl += '&ownerId=' + tagList.currentPersonId;
7711            restUrl += '&entityGuid=' + tagList.entityGuid;
7712            restUrl += '&name=' + encodeURIComponent(tagName);
7713            restUrl += '&includeInactive' + tagList.includeInactive.toString();
7714
7715            if (tagList.categoryGuid && tagList.categoryGuid != '') {
7716                restUrl += '&categoryGuid=' + this.categoryGuid;
7717            }
7718
7719            // only attempt to add tag if an entityGuid exists
7720            if (!tagList.delaySave) {
7721                if (tagList.entityQualifierColumn) {
7722                    restUrl += '/' + tagList.entityQualifierColumn;
7723                }
7724
7725                if (tagList.entityQualifierValue) {
7726                    restUrl += '/' + tagList.entityQualifierValue;
7727                }
7728
7729                $.ajax({
7730                    type: 'POST',
7731                    url: restUrl,
7732                    error: function (xhr, status, error) {
7733                        console.log('AddTag() status: ' + status + ' [' + error + ']: ' + xhr.responseText);
7734                    }
7735                });
7736            }
7737        };
7738
7739        TagList.prototype.removeTag = function (tagName) {
7740
7741            // Since `removeTag` is being called by the jQuery lib, `this`
7742            // is the current TagList DOM element. Get the HTML ID and fetch
7743            // from the cache.
7744            var tagList = exports.tagLists[$(this).attr('id')],
7745                restUrl = Rock.settings.get('baseUrl') + 'api/taggeditems';
7746
7747            // only attempt to remove tag if an entityGuid exists
7748            if (!tagList.delaySave) {
7749
7750                restUrl += '?entityTypeId=' + tagList.entityTypeId;
7751                restUrl += '&ownerId=' + tagList.currentPersonId;
7752                restUrl += '&entityGuid=' + tagList.entityGuid;
7753                restUrl += '&name=' + encodeURIComponent(tagName);
7754                restUrl += '&includeInactive' + tagList.includeInactive.toString();
7755
7756                if (tagList.entityQualifierColumn) {
7757                    restUrl += '&entityQualifier=' + tagList.entityQualifierColumn;
7758                }
7759
7760                if (tagList.entityQualifierValue) {
7761                    restUrl += '&entityQualifierValue=' + tagList.entityQualifierValue;
7762                }
7763
7764                if (tagList.categoryGuid && tagList.categoryGuid != '') {
7765                    restUrl += '&categoryGuid=' + tagList.categoryGuid;
7766                }
7767
7768                $.ajax({
7769                    type: 'DELETE',
7770                    url: restUrl,
7771                    context: {
7772                        tagName: tagName,
7773                        tagsInput: this
7774                    },
7775                    error: function (xhr, status, error) {
7776                        if (xhr && xhr.status == 404) {
7777                            // already deleted
7778                            return;
7779                        }
7780
7781                        Rock.dialogs.alert('Unable to remove tag: ' + error);
7782
7783                        // put the tag back in (in alpha order, case-insensitive)
7784                        var tagsCommaList = $(this.tagsInput).val() + ',' + this.tagName
7785
7786                        tagsCommaList = tagsCommaList.split(',').sort(function (a, b) {
7787                            return a.toLowerCase().localeCompare(b.toLowerCase());
7788                        }).join(',');
7789
7790                        $(this.tagsInput).importTags(tagsCommaList);
7791                        return false;
7792                    }
7793                });
7794            }
7795
7796        };
7797
7798        TagList.prototype.initialize = function () {
7799
7800            var restUrl = Rock.settings.get('baseUrl') + 'api/Tags/AvailableNames';
7801            restUrl += '?entityTypeId=' + this.entityTypeId;
7802            restUrl += '&ownerId=' + this.currentPersonId;
7803            restUrl += '&entityGuid=' + this.entityGuid;
7804            restUrl += '&includeInactive' + this.includeInactive.toString();
7805
7806            if (this.entityQualifierColumn) {
7807                restUrl += '&entityQualifier=' + this.entityQualifierColumn;
7808            }
7809            if (this.entityQualifierValue) {
7810                restUrl += '&entityQualifierValue=' + this.entityQualifierValue;
7811            }
7812            if (this.categoryGuid && this.categoryGuid != '') {
7813                restUrl += '&categoryGuid=' + this.categoryGuid;
7814            }
7815
7816            $('ul.ui-autocomplete').css({ 'width': '300px' });
7817
7818            $('#' + this.controlId).tagsInput({
7819                autocomplete_url: function (request, response) {
7820                    $.ajax({
7821                        url: restUrl + '&name=' + request.term,
7822                        dataType: 'json',
7823                        success: function (data, status, xhr) {
7824                            response($.map(data, function (item) {
7825                                return {
7826                                    value: item.Name,
7827                                    class: !item.OwnerId ? 'system' : 'personal'
7828                                };
7829                            }));
7830                        },
7831                        error: function (xhr, status, error) {
7832                            console.log('availablenames status: ' + status + ' [' + error + ']: ' + xhr.reponseText);
7833                        }
7834                    });
7835                },
7836                autoCompleteAppendTo: 'div.tag-wrap',
7837                autoCompleteMessages: {
7838                    noResults: function () {},
7839                    results: function () {}
7840                },
7841                height: 'auto',
7842                width: '100%',
7843                interactive: true,
7844                defaultText: 'add tag',
7845                removeWithBackspace: false,
7846                onAddTag: this.verifyTag,
7847                onRemoveTag: this.removeTag
7848            });
7849        };
7850
7851        exports = {
7852            tagLists: {},
7853            initialize: function (options) {
7854                if (!options.controlId) throw 'controlId must be set.';
7855                if (!options.entityTypeId) throw 'entityTypeId must be set.';
7856                if (!options.currentPersonId) throw 'currentPersonId must be set';
7857                if (!options.entityGuid) throw 'entityGuid must be set';
7858
7859                var tagList = new TagList(options);
7860                tagList.options = options;
7861                exports.tagLists[options.controlId] = tagList;
7862                tagList.initialize();
7863            }
7864        };
7865
7866        return exports;
7867    }());
7868}(jQuery));
7869;
7870(function ($) {
7871    'use strict';
7872    window.Rock = window.Rock || {};
7873    Rock.controls = Rock.controls || {};
7874
7875    Rock.controls.taskActivityProgressReporter = (function () {
7876        const TaskActivityProgressReporter = function (options) {
7877            if (!Rock.RealTime) {
7878                throw new Error("realtime.js must be included first.");
7879            }
7880
7881            if (!options.controlId) {
7882                throw new Error("controlId is required.");
7883            }
7884
7885            this.controlId = options.controlId;
7886            this.connectionId = undefined;
7887
7888            this.connect();
7889        }
7890
7891        TaskActivityProgressReporter.prototype.getTaskId = function () {
7892            const control = document.getElementById(this.controlId);
7893
7894            if (!control) {
7895                return null;
7896            }
7897
7898            return control.getAttribute("data-task-id");
7899        };
7900
7901        TaskActivityProgressReporter.prototype.connect = async function () {
7902            this.topic = await Rock.RealTime.getTopic("Rock.RealTime.Topics.TaskActivityProgressTopic");
7903
7904            this.topic.on("taskStarted", this.onTaskStarted.bind(this));
7905            this.topic.on("taskCompleted", this.onTaskCompleted.bind(this));
7906            this.topic.on("updateTaskProgress", this.onUpdateTaskProgress.bind(this));
7907
7908            const $connectionId = $(`#${this.controlId} [id$='_hfConnectionId']`);
7909
7910            $connectionId.val(this.topic.connectionId || "");
7911        };
7912
7913        TaskActivityProgressReporter.prototype.onTaskStarted = function (status) {
7914            // Intentionally left blank for now.
7915        };
7916
7917        TaskActivityProgressReporter.prototype.onTaskCompleted = function (status) {
7918            const taskId = this.getTaskId();
7919
7920            if (status.taskId !== taskId) {
7921                return;
7922            }
7923
7924            const $preparing = $(`#${this.controlId} .js-preparing`);
7925            const $progress = $(`#${this.controlId} .js-progress-div`);
7926            const $results = $(`#${this.controlId} .js-results`);
7927
7928            $results.removeClass("alert-danger").removeClass("alert-warning").removeClass("alert-success");
7929
7930            if (status.errors && status.errors.length > 0) {
7931                console.error(status.errors);
7932                $results.addClass("alert-danger");
7933            }
7934            else if (status.warnings && status.warnings.length > 0) {
7935                console.warn(status.warnings);
7936                $results.addClass("alert-warning");
7937            }
7938            else {
7939                $results.addClass("alert-success");
7940            }
7941
7942            $results.text(status.message).slideDown();
7943            $preparing.slideUp();
7944            $progress.slideUp();
7945        };
7946
7947        TaskActivityProgressReporter.prototype.onUpdateTaskProgress = function (progress) {
7948            const taskId = this.getTaskId();
7949
7950            if (progress.taskId !== taskId) {
7951                return;
7952            }
7953
7954            const $preparing = $(`#${this.controlId} .js-preparing`);
7955            const $progress = $(`#${this.controlId} .js-progress-div`);
7956            const $bar = $(`#${this.controlId} .js-progress-bar`);
7957
7958            $bar.prop("aria-valuenow", progress.completionPercentage);
7959            $bar.prop("aria-valuemax", "100");
7960            $bar.css("width", `${progress.completionPercentage}%`);
7961
7962            if (progress.message) {
7963                $bar.text(progress.message);
7964            }
7965            else {
7966                $bar.text(`${progress.completionPercentage}%`);
7967            }
7968
7969            $progress.slideDown();
7970            $preparing.slideUp();
7971        };
7972
7973        const reporters = {};
7974
7975        const exports = {
7976            initialize(options) {
7977                if (!options.controlId) {
7978                    throw new Error("id is required.");
7979                }
7980
7981                if (reporters[options.controlId]) {
7982                    return;
7983                }
7984
7985                reporters[options.controlId] = new TaskActivityProgressReporter(options);
7986            }
7987        };
7988
7989        return exports;
7990    }());
7991}(jQuery));
7992;
7993(function ($) {
7994    'use strict';
7995    window.Rock = window.Rock || {};
7996    Rock.controls = Rock.controls || {};
7997
7998    Rock.controls.timePicker = (function () {
7999        var exports = {
8000            initialize: function (options) {
8001                if (!options.id) {
8002                    throw 'id is required';
8003                }
8004
8005                var $tp = $('#' + options.id);
8006
8007                // bootstrap-timepicker requires that the parent div have bootstrap-timepicker, input-append classes
8008                $tp.closest('div').addClass('bootstrap-timepicker').addClass('input-append');
8009
8010                // uses https://github.com/jdewit/bootstrap-timepicker
8011                $tp.timepicker({
8012                    defaultTime: false
8013                });
8014
8015                $tp.closest('.js-timepicker-input').find('.js-timepicker-clear').on('click', function () {
8016                    $tp.timepicker('clear');
8017                });
8018            }
8019        };
8020
8021        return exports;
8022    }());
8023}(jQuery));
8024;
8025(function ($) {
8026    'use strict';
8027    window.Rock = window.Rock || {};
8028    Rock.controls = Rock.controls || {};
8029
8030    Rock.controls.toggleButton = (function () {
8031        var exports = {
8032            initialize: function (options) {
8033                if (!options.id) {
8034                    throw 'id is required';
8035                }
8036
8037                // uses pattern from http://www.bootply.com/92189
8038
8039                $('#' + options.id + ' .btn-toggle').on('click', function (e) {
8040
8041                    e.stopImmediatePropagation();
8042
8043                    $(this).find('.btn').toggleClass('active');
8044
8045                    if (options.activeButtonCssClass && $(this).find('.' + options.activeButtonCssClass).length > 0) {
8046                        $(this).find('.btn').toggleClass(options.activeButtonCssClass);
8047                    }
8048
8049                    if (options.onButtonCssClass) {
8050                        $(this).find('.js-toggle-on').toggleClass(options.onButtonCssClass);
8051                    }
8052
8053                    if (options.offButtonCssClass) {
8054                        $(this).find('.js-toggle-off').toggleClass(options.offButtonCssClass);
8055                    }
8056
8057                    $(this).parent().find('.js-toggle-checked').val($(this).find('.js-toggle-on').hasClass('active'));
8058
8059                });
8060
8061            }
8062        };
8063
8064        return exports;
8065    }());
8066}(jQuery));
8067;
8068(function ($) {
8069    'use strict';
8070    window.Rock = window.Rock || {};
8071    Rock.controls = Rock.controls || {};
8072
8073    Rock.controls.universalItemSearchPicker = (function () {
8074        var UniversalItemSearchPicker = function (options) {
8075            this.controlId = options.controlId;
8076            this.restUrl = options.restUrl;
8077            this.context = options.context;
8078            this.areDetailsAlwaysVisible = options.areDetailsAlwaysVisible;
8079            this.iScroll = null;
8080            this.$pickerControl = $('#' + this.controlId);
8081            this.$pickerScrollContainer = this.$pickerControl.find('.js-universalitemsearchpicker-scroll-container');
8082        };
8083
8084        UniversalItemSearchPicker.prototype.initializeEventHandlers = function () {
8085            var controlId = this.controlId;
8086            var restUrl = this.restUrl;
8087            var context = this.context;
8088            var areDetailsAlwaysVisible = this.areDetailsAlwaysVisible;
8089
8090            var $pickerControl = this.$pickerControl;
8091            var $searchFields = $pickerControl.find('input.js-universalitemsearchpicker-search-field');
8092            var $searchFieldName = $pickerControl.find('input.js-universalitemsearchpicker-search-name');
8093            var $searchFieldIncludeInactive = $pickerControl.find("input.js-include-inactive");
8094            var $searchResults = $pickerControl.find('.js-universalitemsearchpicker-searchresults');
8095            var $pickerToggle = $pickerControl.find('.js-universalitemsearchpicker-toggle');
8096            var $pickerMenu = $pickerControl.find('.js-universalitemsearchpicker-menu');
8097            var $pickerSelect = $pickerControl.find('.js-universalitemsearchpicker-select');
8098            var $pickerSelectNone = $pickerControl.find('.js-picker-select-none');
8099            var $pickerItemValue = $pickerControl.find('.js-item-value');
8100            var $pickerItemName = $pickerControl.find('.js-item-name');
8101            var $pickerCancel = $pickerControl.find('.js-universalitemsearchpicker-cancel');
8102            var promise = null;
8103            var lastSelectedItemValue = null;
8104
8105            var autoCompletes = $searchFields.autocomplete({
8106                source: function (request, response) {
8107
8108                    var search = {
8109                        value: $searchFieldName.val()
8110                    };
8111
8112                    // make sure that at least one of the search fields has 3 chars in it
8113                    if (search.value.length < 3) {
8114                        return;
8115                    }
8116
8117                    // abort any searches that haven't returned yet, so that we don't get a pile of results in random order
8118                    if (promise && promise.state() === 'pending') {
8119                        promise.abort();
8120                    }
8121
8122                    var searchBag = {
8123                        context: context,
8124                        value: search.value,
8125                        isInactiveIncluded: $searchFieldIncludeInactive && $searchFieldIncludeInactive.is(":checked") == true
8126                    };
8127
8128                    // set the timeout to 20 seconds, just in case it takes a long time to search
8129                    promise = $.ajax({
8130                        url: restUrl,
8131                        method: "POST",
8132                        data: JSON.stringify(searchBag),
8133                        contentType: "application/json",
8134                        timeout: 20000,
8135                        dataType: 'json'
8136                    });
8137
8138                    // Display a wait indicator to show that the search is now running.
8139                    if ($('.js-searching-notification').length == 0) {
8140                        $searchResults.prepend('<i class="ti ti-refresh ti-spin margin-l-md js-searching-notification" style="display: none; opacity: .4;"></i>');
8141                    }
8142                    $('.js-searching-notification').fadeIn(800);
8143
8144                    promise.done(function (data) {
8145                        $searchResults.html('');
8146
8147                        response(data);
8148
8149                        exports.universalItemSearchPickers[controlId].updateScrollbar();
8150                    });
8151
8152                    promise.fail(function (xhr, status, error) {
8153                        console.log(status + ' [' + error + ']: ' + xhr.responseText);
8154                        var errorCode = xhr.status;
8155                        if (errorCode == 401) {
8156                            $searchResults.html("<li class='text-danger'>Sorry, you're not authorized to search.</li>");
8157                        }
8158
8159                        $('.js-searching-notification').remove();
8160                    });
8161                },
8162                // Set minLength to 0, but check that at least one field has 3 chars before fetching from REST.
8163                // To minimize load on the server, don't trigger the search until a reasonable delay after the last keypress.
8164                minLength: 0,
8165                delay: 750,
8166                html: true,
8167                appendTo: $searchResults,
8168                pickerControlId: controlId,
8169                messages: {
8170                    noResults: function () { },
8171                    results: function () { }
8172                }
8173            });
8174
8175            var autoCompleteCustomRenderItem = function ($ul, item) {
8176                // override jQueryUI autocomplete's _renderItem so that we can do HTML for the ListItems
8177                // derived from http://github.com/scottgonzalez/jquery-ui-extensions
8178
8179                var $div = $('<div/>').attr('class', 'radio');
8180
8181                var $labelText = $('<span/>')
8182                    .addClass('label-text d-flex gap')
8183                    .append($('<span/>').addClass('flex-grow-1').text(item.title));
8184
8185                if (item.labels) {
8186                    var $labels = $('<span/>').addClass('d-flex gap');
8187
8188                    for (let labelIndex = 0; labelIndex < item.labels.length; labelIndex++) {
8189                        $('<span/>')
8190                            .text(item.labels[labelIndex].text)
8191                            .addClass('label label-' + item.labels[labelIndex].value)
8192                            .appendTo($labels);
8193                    }
8194
8195                    $labelText.append($labels);
8196                }
8197
8198                var $label = $('<label/>')
8199                    .append($labelText)
8200                    .prependTo($div);
8201
8202                $('<input type="radio" name="item-id" />')
8203                    .attr('value', item.Id)
8204                    .prependTo($label);
8205
8206                var $li = $('<li/>')
8207                    .addClass('picker-select-item js-picker-select-item')
8208                    .css('list-style', 'none')
8209                    .attr('data-item-value', item.value)
8210                    .attr('data-item-name', item.title)
8211                    .html($div);
8212
8213                var $resultSection = $(this.options.appendTo);
8214
8215                var $itemDetailsDiv = $('<div/>')
8216                    .addClass('picker-select-item-details js-picker-select-item-details clearfix');
8217
8218                if (item.description) {
8219                    var $descriptionDiv = $('<div/>');
8220
8221                    $descriptionDiv.addClass("picker-select-item-description mb-2");
8222                    $descriptionDiv.text(item.description);
8223
8224                    $itemDetailsDiv.append($descriptionDiv);
8225                }
8226
8227                if (item.details) {
8228                    for (let detailIndex = 0; detailIndex < item.details.length; detailIndex++) {
8229                        var detail = item.details[detailIndex];
8230                        var $detailDl = $('<dl/>').addClass("d-flex");
8231                        $detailDl.append($("<dt/>").addClass("mr-2 text-nowrap").text(detail.value));
8232                        $detailDl.append($("<dd/>").text(detail.text));
8233
8234                        $itemDetailsDiv.append($detailDl);
8235                    }
8236                }
8237
8238                if (!areDetailsAlwaysVisible) {
8239                    $itemDetailsDiv.hide();
8240                }
8241
8242                $itemDetailsDiv.appendTo($li);
8243
8244                if (item.isInactive) {
8245                    $li.addClass('is-inactive');
8246                }
8247
8248                return $resultSection.append($li);
8249            }
8250
8251            // each search field has its own autocomplete object, so we'll need override with our custom _renderItem to each
8252            $.each(autoCompletes, function (a, b, c) {
8253                var autoComplete = $(autoCompletes[a]).data('ui-autocomplete');
8254
8255                // Debugging: override close to prevent it canceling when loosing focus when debugging
8256                // autoComplete.close = function () { };
8257
8258                autoComplete._renderItem = autoCompleteCustomRenderItem;
8259            });
8260
8261            $pickerToggle.on('click', function (e) {
8262                e.preventDefault();
8263                $(this).toggleClass("active");
8264                $pickerMenu.toggle(0, function () {
8265                    exports.universalItemSearchPickers[controlId].updateScrollbar();
8266                    $searchFieldName.trigger('focus');
8267                });
8268            });
8269
8270            $pickerControl.on('click', '.js-picker-select-item', function (e) {
8271                if (e.type == 'click' && $(e.target).is(':input') == false) {
8272                    // only process the click event if it has bubbled up to the input tag
8273                    return;
8274                }
8275
8276                e.stopPropagation();
8277
8278                var $selectedItem = $(this).closest('.js-picker-select-item');
8279                var $itemDetails = $selectedItem.find('.js-picker-select-item-details');
8280
8281                var selectedItemValue = $selectedItem.attr('data-item-value');
8282
8283                if ($itemDetails.is(':visible')) {
8284                    if (selectedItemValue == lastSelectedItemValue && e.type == 'click') {
8285                        // if they are clicking the same item twice in a row
8286                        // (and the details are done expanding), assume that's
8287                        // the one they want to pick
8288                        $pickerSelect.trigger('click');
8289                    } else {
8290                        // if it is already visible but isn't the same one twice, just leave it open
8291                    }
8292                }
8293
8294                lastSelectedItemValue = selectedItemValue;
8295
8296                showItemDetails($selectedItem.find('.picker-select-item-details:hidden'));
8297            });
8298
8299            var showItemDetails = function ($itemDetails) {
8300                if ($itemDetails.length) {
8301                    $itemDetails.slideDown(function () {
8302                        exports.universalItemSearchPickers[controlId].updateScrollbar();
8303                    });
8304                }
8305            }
8306
8307            $pickerControl.on('mouseenter',
8308                function () {
8309                    // only show the X if there is something picked
8310                    if (($pickerItemValue.val() || '') !== '') {
8311                        $pickerSelectNone.addClass('show-hover');
8312                    }
8313                });
8314
8315            $pickerCancel.on('click', function () {
8316                clearSearchFields();
8317                $pickerMenu.slideUp(function () {
8318                    exports.universalItemSearchPickers[controlId].updateScrollbar();
8319                });
8320            });
8321
8322            $pickerSelectNone.on('click', function (e) {
8323                // prevent the click from bubbling up to the pickerControl click event
8324                e.preventDefault();
8325                e.stopPropagation();
8326
8327                $pickerItemValue.val('');
8328                $pickerItemName.val('');
8329                // run onclick event from the button
8330                $(this).trigger('onclick');
8331            });
8332
8333            // disable the enter key : this will prevent the enter key from clearing the loaded search query.
8334            $pickerControl.on('keypress', function (e) {
8335                if (e.which == 13) {
8336                    return false;
8337                }
8338            });
8339
8340            var setSelectedItem = function (selectedValue, selectedText) {
8341                var selectedItemLabel = $pickerControl.find('.js-universalitemsearchpicker-selecteditem-label');
8342
8343                $pickerItemValue.val(selectedValue);
8344                $pickerItemName.val(selectedText);
8345
8346                selectedItemLabel.val(selectedValue);
8347                selectedItemLabel.text(selectedText);
8348
8349                $pickerMenu.slideUp();
8350            }
8351
8352            var clearSearchFields = function () {
8353                $searchFieldName.val('');
8354            }
8355
8356            $pickerSelect.on('click', function () {
8357                var $selectedItem = $pickerControl.find('input:checked').closest('.js-picker-select-item');
8358                var selectedItemValue = $selectedItem.attr('data-item-value');
8359                var selectedText = $selectedItem.attr('data-item-name');
8360
8361                setSelectedItem(selectedItemValue, selectedText);
8362                clearSearchFields();
8363
8364                // Fire the postBack for the Select button.
8365                var postBackUrl = $pickerSelect.prop('href');
8366                if (postBackUrl) {
8367                    window.location = postBackUrl;
8368                }
8369            });
8370        };
8371
8372        UniversalItemSearchPicker.prototype.updateScrollbar = function () {
8373            var self = this;
8374
8375            // first, update this control's scrollbar, then the modal's
8376
8377            if (self.$pickerScrollContainer.is(':visible')) {
8378                if (self.iScroll) {
8379                    self.iScroll.refresh();
8380                }
8381            }
8382
8383            // update the outer modal scrollbar
8384            Rock.dialogs.updateModalScrollBar(this.controlId);
8385        }
8386
8387        UniversalItemSearchPicker.prototype.initialize = function () {
8388            this.iScroll = new IScroll($('.viewport', this.$pickerControl)[0], {
8389                mouseWheel: true,
8390                indicators: {
8391                    el: $('.track', this.$pickerScrollContainer)[0],
8392                    interactive: true,
8393                    resize: false,
8394                    listenY: true,
8395                    listenX: false,
8396                },
8397                click: false,
8398                preventDefaultException: { tagName: /.*/ }
8399            });
8400
8401            this.initializeEventHandlers();
8402        };
8403
8404        var exports = {
8405            universalItemSearchPickers: {},
8406            findControl: function (controlId) {
8407                return exports.universalItemSearchPickers[controlId];
8408            },
8409            initialize: function (options) {
8410                if (!options.controlId) throw '`controlId` is required.';
8411                if (!options.restUrl) throw '`restUrl` is required.';
8412
8413                var picker = new UniversalItemSearchPicker(options);
8414                exports.universalItemSearchPickers[options.controlId] = picker;
8415                picker.initialize();
8416            }
8417        };
8418        return exports;
8419    }());
8420}(jQuery));
8421;
8422(function ($) {
8423    'use strict';
8424    window.Rock = window.Rock || {};
8425    Rock.controls = Rock.controls || {};
8426
8427    Rock.controls.util = (function () {
8428        var exports = {
8429            googleMapsIsLoaded: function () {
8430                // trigger googleMapsIsLoaded so that controls (like the geoPicker) know about it
8431                $(window).trigger('googleMapsIsLoaded');
8432            },
8433            loadGoogleMapsApi: function (apiSrc) {
8434                // ensure that js for googleMapsApi is added to page
8435                if (!$('#googleMapsApi').length) {
8436                    // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this
8437                    $.ajaxSetup({ cache: true });
8438                    var src = apiSrc + '&callback=Rock.controls.util.googleMapsIsLoaded';
8439                    $('head').prepend("<script id='googleMapsApi' src='" + src + "' />");
8440                } else if (typeof google == 'object' && typeof google.maps == 'object') {
8441                    this.googleMapsIsLoaded();
8442                }
8443            },
8444          // 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
8445          // 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
8446            getValueAsPixels: function (a)
8447            {
8448              var floatValue = parseFloat(a);
8449              if (floatValue) {
8450                return floatValue + 'px';
8451              }
8452              else {
8453                return '';
8454              }
8455            },
8456            getScrollbarWidth: function () {
8457                // thx d.walsh
8458                var scrollDiv = document.createElement('div');
8459                scrollDiv.className = 'modal-scrollbar-measure';
8460                document.body.appendChild(scrollDiv);
8461                var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
8462                document.body.removeChild(scrollDiv);
8463                return scrollbarWidth;
8464            } // Static
8465        };
8466
8467        return exports;
8468
8469    }());
8470}(jQuery));
8471;
8472(function ($) {
8473    'use strict';
8474    window.Rock = window.Rock || {};
8475    Rock.controls = Rock.controls || {};
8476    const DefaultComparisonTypes = [
8477        { Value: 1, Text: 'Equal To' },
8478        { Value: 2, Text: 'Not Equal To' },
8479        { Value: 4, Text: 'Starts With' },
8480        { Value: 8, Text: 'Contains' },
8481        { Value: 16, Text: 'Does Not Contain' },
8482        { Value: 32, Text: 'Is Blank' },
8483        { Value: 64, Text: 'Is Not Blank' },
8484        { Value: 2048, Text: 'Ends With' }
8485    ];
8486    const GroupAll = 1;
8487    const GroupAny = 2;
8488    const GroupAllFalse = 3;
8489    const GroupAnyFalse = 4;
8490
8491    Rock.controls.valueFilter = (function () {
8492        //
8493        // Setup the UI so it reflects the initial data configured on the filter.
8494        //
8495        function loadInitialData($filter) {
8496            var data;
8497
8498            //
8499            // Try to parse the initial data.
8500            //
8501            try {
8502                data = JSON.parse($filter.find('input[type="hidden"]').val());
8503            }
8504            catch (ex) {
8505                data = null;
8506            }
8507
8508            //
8509            // Initialize a new filter if we have no current settings.
8510            //
8511            if (data === null) {
8512                data = { ExpressionType: GroupAny, Filters: [] };
8513                $filter.find('input[type="hidden"]').val(JSON.stringify(data));
8514            }
8515
8516            //
8517            // Grab the objects we need from the filter control.
8518            //
8519            var $filterTypeAll = $filter.data('filterTypeAll');
8520            var $filterTypeAny = $filter.data('filterTypeAny');
8521            var options = $filter.data('options');
8522
8523            //
8524            // Set the initial state of the Any/All toggle buttons.
8525            //
8526            if (data.ExpressionType === GroupAll) {
8527                $filterTypeAll.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
8528                $filterTypeAny.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);
8529            }
8530            else {
8531                $filterTypeAny.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
8532                $filterTypeAll.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);
8533            }
8534
8535            //
8536            // Add the initial field rows.
8537            //
8538            for (var index in data.Filters) {
8539                var expression = data.Filters[index];
8540
8541                addFilterRow($filter, expression.Value, expression.Comparison);
8542            }
8543
8544            if (data.Filters.length === 0) {
8545                addFilterRow($filter, '', 8);
8546                updateData($filter);
8547            }
8548        }
8549
8550        //
8551        // Setup the initial event handlers for the controls in filter.
8552        //
8553        function addControlEvents($filter) {
8554            //
8555            // Load all the objects we need from the filter.
8556            //
8557            var $filterTypeAll = $filter.data('filterTypeAll');
8558            var $filterTypeAny = $filter.data('filterTypeAny');
8559            var options = $filter.data('options');
8560
8561            //
8562            // Add a click handler on the "Any" toggle button that will turn off the
8563            // "All" option and turn on the "Any" option instead.
8564            //
8565            $filterTypeAny.on('click', function (e) {
8566                e.preventDefault();
8567
8568                $filterTypeAny.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
8569                $filterTypeAll.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);
8570
8571                updateData($filter);
8572            });
8573
8574            //
8575            // Add a click handler on the "All" toggle button that will turn off the
8576            // "Any" option and turn on the "All" option instead.
8577            //
8578            $filterTypeAll.on('click', function (e) {
8579                e.preventDefault();
8580
8581                $filterTypeAll.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass);
8582                $filterTypeAny.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass);
8583
8584                updateData($filter);
8585            });
8586
8587            //
8588            // Add a click handler to the "Add" button that will add a new filter row.
8589            //
8590            $filter.data('addButton').on('click', function (e) {
8591                e.preventDefault();
8592                addFilterRow($filter, '', 8);
8593                updateData($filter);
8594            });
8595        }
8596
8597        //
8598        // Add a new field row.
8599        //
8600        function addFilterRow($filter, value, type) {
8601            var options = $filter.data('options');
8602            var $comparisonButton = null;
8603
8604            //
8605            // Create the row container as an input-group so things look pretty.
8606            //
8607            var $row = $('<div class="input-group margin-b-sm"></div>');
8608
8609            //
8610            // Create the text field the user can type into.
8611            //
8612            var $text = $('<input type="text" class="form-control">').appendTo($row);
8613
8614            //
8615            // Create the drop down that indicates what type of filter row this is.
8616            //
8617            var $inputGroupBtns = $('<span class="input-group-btn" />').appendTo($row);
8618            var $fieldTypeBtn = $('<button type="button" class="btn btn-default dropdown-toggle filter-dropdown-toggle" data-toggle="dropdown"><span /> <span class="caret" /></button>')
8619                .appendTo($inputGroupBtns);
8620            var $fieldTypeOptions = $('<ul class="dropdown-menu dropdown-menu-right"></ul>')
8621                .appendTo($inputGroupBtns);
8622            for (var i in options.comparisonTypes) {
8623                var $btn = $('<li><a href="#" data-value="' + options.comparisonTypes[i].Value + '">' + options.comparisonTypes[i].Text + '</a></li>').appendTo($fieldTypeOptions);
8624                if (type === options.comparisonTypes[i].Value) {
8625                    $comparisonButton = $btn;
8626                }
8627            }
8628            $comparisonButton = $comparisonButton || $fieldTypeOptions.find('li:first-child');
8629
8630            //
8631            // Create the delete button.
8632            //
8633            $('<button type="button" class="btn btn-danger btn-square"><i class="ti ti-x"></i></button>')
8634                .appendTo($inputGroupBtns);
8635
8636            //
8637            // Set default values.
8638            //
8639            $text.val(value);
8640            changeFilterRowType($filter, $comparisonButton, true);
8641
8642            //
8643            // Setup event handlers.
8644            //
8645            addFilterRowEvents($filter, $row);
8646
8647            //
8648            // Append this new filter row to the DOM.
8649            //
8650            $filter.data('rowContainer').append($row);
8651        }
8652
8653        //
8654        // Setup the event handlers for a newly created filter row.
8655        //
8656        function addFilterRowEvents($filter, $field) {
8657            //
8658            // When the text has changed, update the stored data.
8659            //
8660            $field.find('input[type="text"]').on('change', function () {
8661                updateData($filter);
8662            });
8663
8664            //
8665            // When the user clicks on one of the filter type drop down options, update
8666            // the filter row type.
8667            //
8668            $field.find('ul > li > a').on('click', function (e) {
8669                e.preventDefault();
8670                changeFilterRowType($filter, $(this));
8671            });
8672
8673            //
8674            // Remove a single row from the filter.
8675            //
8676            $field.find('button.btn-danger').on('click', function (e) {
8677                e.preventDefault();
8678                $(this).closest('.input-group').remove();
8679                updateData($filter);
8680            });
8681        }
8682
8683        //
8684        // Update the filter row type based on what the user clicked.
8685        //
8686        function changeFilterRowType($filter, $btn, skipUpdate) {
8687            var $btnGroup = $btn.closest('.input-group-btn');
8688            $btnGroup.find('.dropdown-toggle span:first-child').text($btn.text());
8689
8690            if ($btn.data('value') === 32 || $btn.data('value') === 64) {
8691                $filter.find('input[type="text"]').val('').prop('disabled', true);
8692            }
8693            else {
8694                $filter.find('input[type="text"]').prop('disabled', false);
8695            }
8696
8697            if (skipUpdate !== true) {
8698                updateData($filter);
8699            }
8700        }
8701
8702        //
8703        // Update the hidden field to match what is in the UI.
8704        //
8705        function updateData($filter) {
8706            var options = $filter.data('options');
8707            var filters = [];
8708
8709            //
8710            // Walk each row of the filter and build up the filters collection.
8711            //
8712            $filter.data('rowContainer').find('.input-group').each(function (index, element) {
8713                var $field = $(element);
8714                var text = $field.find('input[type="text"]').val();
8715                var typeText = $field.find('button.dropdown-toggle span:first-child').text();
8716                var type = $field.find('ul li a').filter(function () { return $(this).text() === typeText; }).data('value');
8717
8718                filters.push({ Value: text, Comparison: type });
8719            });
8720
8721            //
8722            // If there is only one filter, it is a Contains filter and has no value, then get rid of it.
8723            //
8724            if (filters.length === 1 && filters[0].Value === '' && filters[0].Comparison === 8) {
8725                filters = [];
8726            }
8727
8728            //
8729            // Build the final filtered text value.
8730            //
8731            var data = {
8732                ExpressionType: $filter.data('filterTypeAny').hasClass(options.btnToggleOnClass) ? GroupAny : GroupAll,
8733                Filters: filters
8734            };
8735
8736            $filter.find('input[type="hidden"]').val(JSON.stringify(data));
8737        }
8738
8739        var exports = {
8740            initialize: function (options) {
8741                if (!options.controlId) {
8742                    throw 'controlId is required';
8743                }
8744
8745                //
8746                // Make sure we have minimum default options
8747                //
8748                options = $.extend({
8749                    btnToggleOnClass: 'btn-info',
8750                    btnToggleOffClass: 'btn-default',
8751                    required: true,
8752                    requiredMessage: 'The field is required',
8753                    hideFilterMode: false,
8754                    comparisonTypes: DefaultComparisonTypes,
8755                    defaultComparison: 8
8756                }, options);
8757
8758                //
8759                // Setup the comparsionTypesByValue object.
8760                //
8761                options.comparisonTypeByValue = {};
8762                for (var i = 0; i < options.comparisonTypes.length; i++) {
8763                    options.comparisonTypeByValue[options.comparisonTypes[i].Value] = options.comparisonTypes[i].Text;
8764                }
8765
8766                //
8767                // Find the filter control placeholder.
8768                //
8769                var $filter = $('#' + options.controlId);
8770
8771                //
8772                // Setup the "Any/All" buttons for the filter.
8773                //
8774                var $filterTypeContainer = $('<div class="text-right" />').appendTo($filter);
8775                var $filterTypeGroup = $('<div class="btn-group" role="group" />').appendTo($filterTypeContainer);
8776                var $filterTypeAny = $('<button type="button" class="btn btn-default btn-xs">Any</button>').appendTo($filterTypeGroup);
8777                var $filterTypeAll = $('<button type="button" class="btn btn-default btn-xs">All</button>').appendTo($filterTypeGroup);
8778                if (options.hideFilterMode === true) {
8779                    $filterTypeContainer.addClass('hidden');
8780                }
8781
8782                //
8783                // Setup the placeholder div that will contain all the filter  rows.
8784                //
8785                var $rowContainer = $('<div />').appendTo($filter);
8786
8787                //
8788                // Setup the add button.
8789                //
8790                var $addButtonContainer = $('<div class="text-right" />').appendTo($filter);
8791                var $addButton = $('<a href="#" class="btn btn-default btn-square btn-sm"><i class="ti ti-plus"></i></a>').appendTo($addButtonContainer);
8792
8793                //
8794                // Store all the objects we need access to later as jQuery data objects
8795                // on the filter control.
8796                //
8797                $filter.data('options', options);
8798                $filter.data('filterTypeAny', $filterTypeAny);
8799                $filter.data('filterTypeAll', $filterTypeAll);
8800                $filter.data('rowContainer', $rowContainer);
8801                $filter.data('addButton', $addButton);
8802
8803                //
8804                // Setup initial control events.
8805                //
8806                addControlEvents($filter);
8807
8808                //
8809                // Setup UI to reflect initial data.
8810                //
8811                loadInitialData($filter);
8812            },
8813            clientValidate: function (validator, args) {
8814                var $filter = $(validator).prev();
8815                var required = $filter.data('options').required;
8816                var data = JSON.parse($filter.find('input[type="hidden"]').val());
8817
8818                var isValid = !required || data.Filters.length > 0;
8819
8820                if (isValid) {
8821                    $filter.closest('.form-group').removeClass('has-error');
8822                    args.IsValid = true;
8823                }
8824                else {
8825                    $filter.closest('.form-group').addClass('has-error');
8826                    args.IsValid = false;
8827                    validator.errormessage = $filter.data('options').requiredMessage;
8828                }
8829            }
8830        };
8831
8832        return exports;
8833    }());
8834}(jQuery));
8835;
8836; (function () {
8837  function updateKeyValues(e) {
8838    var $span = e.closest('span.value-list');
8839    var newValue = '';
8840
8841    var valueDelimiters = ['|', ','];
8842    $span.children('span.value-list-rows').first().children('div.controls-row').each(function (index) {
8843      var value = $(this).children('.js-value-list-input').first().val();
8844
8845      valueDelimiters.forEach(function (v, i, a) {
8846        var re = new RegExp('\\' + v, 'g');
8847        if (value.indexOf(v) > -1) {
8848          value = value.replace(re, encodeURIComponent(v));
8849        }
8850      });
8851      newValue += value + '|'
8852    });
8853
8854    $span.children('input').first().val(newValue);
8855  }
8856
8857  Sys.Application.add_load(function () {
8858
8859    $('a.value-list-add').on('click', function (e) {
8860      e.preventDefault();
8861      var $ValueList = $(this).closest('.value-list');
8862      var newValuePickerHtml = $ValueList.find('.js-value-list-html').val()
8863      $ValueList.find('.value-list-rows').append(newValuePickerHtml);
8864      updateKeyValues($(this));
8865      Rock.controls.modal.updateSize($(this));
8866    });
8867
8868    $(document).on('click', 'a.value-list-remove', function (e) {
8869      e.preventDefault();
8870      var $rows = $(this).closest('span.value-list-rows');
8871      $(this).closest('div.controls-row').remove();
8872      updateKeyValues($rows);
8873      Rock.controls.modal.updateSize($(this));
8874    });
8875
8876    $(document).on('change', '.js-value-list-input', function (e) {
8877      updateKeyValues($(this));
8878    });
8879  });
8880})();
8881;
8882(function (Sys) {
8883    'use strict';
8884    Sys.Application.add_load(function () {
8885        $('a.warning').on('click', function (e) {
8886            e.preventDefault();
8887            $(this).siblings('div.alert-warning').slideToggle(function () {
8888                Rock.controls.modal.updateSize(this);
8889            });
8890            $(this).siblings('div.alert-info').slideUp();
8891        });
8892    });
8893}(Sys));
8894;
8895/* NOTE: Requires this on the init of the block
8896
8897RockPage.AddScriptLink("~/Scripts/d3-cloud/d3.layout.cloud.js");
8898RockPage.AddScriptLink("~/Scripts/d3-cloud/d3.min.js");
8899
8900*/
8901
8902(function ($)
8903{
8904  'use strict';
8905  window.Rock = window.Rock || {};
8906  Rock.controls = Rock.controls || {};
8907
8908  Rock.controls.wordcloud = (function ()
8909  {
8910    
8911    var exports = {
8912      initialize: function (options)
8913      {
8914        if (!options.inputTextId) {
8915          throw 'inputTextId is required';
8916        }
8917
8918        if (!options.visId) {
8919          throw 'visId is required';
8920        }
8921
8922        // if the visId element isn't found, jump out
8923        if (d3.select("#" + options.visId).empty()) {
8924          return;
8925        }
8926
8927        var $inputText = $('#' + options.inputTextId);
8928        var visId = options.visId;
8929        var statusId = options.statusId;
8930        var svgWidth = options.width || 800;
8931        var svgHeight = options.height || 300;
8932        var fontName = options.fontName || 'Impact';
8933        var maxWords = options.maxWords || 255;
8934        var scaleName = options.scaleName || 'log';
8935        var spiralName = options.spiralName || 'archimedean';
8936        var colors = options.colors || ['#0193B9', '#F2C852', '#1DB82B', '#2B515D', '#ED3223'];
8937
8938        var angleCount = options.angleCount == undefined ? 6 : options.angleCount;
8939        if (angleCount > 180) {
8940          angleCount = 180;
8941        }
8942
8943        // a good max range is -90 to 90 so that words don't go upside down
8944        var angleMin = options.angleMin == undefined ? -90 : options.angleMin;
8945        var angleMax = options.angleMax == undefined ? 90 : options.angleMax;
8946        var angles = [];
8947        if (angleCount == 1) {
8948          // one angle, so choose the midpoint of the angleMin and angleMax
8949          angles.push((angleMax + angleMin) / 2);
8950        }
8951        else if (angleCount == 2) {
8952          // two angles, so choose the angleMin and angleMax
8953          angles.push(angleMin);
8954          angles.push(angleMax);
8955        }
8956        else if (angleCount > 2) {
8957          // three or more, so choose the angleMin and angleMax, and the the remaining ones between
8958          var angleSize = (angleMax - angleMin) / (angleCount - 1)
8959          var angle = angleMin;
8960          while (angle <= angleMax) {
8961            angles.push(angle);
8962            angle += angleSize;
8963          }
8964        }
8965
8966        /* from cloud.js */
8967        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",
8968            fill = d3.scaleLinear()
8969            .domain([0, 1, 2, 3, 4, 5, 6, 10, 15, 20, 100])
8970            .range(d3.schemeCategory20b),
8971            w = svgWidth,
8972            h = svgHeight,
8973            words = [],
8974            max, scale = 1,
8975            complete = 0,
8976            keyword = "",
8977            tags, fontSize, maxLength = 30,
8978            fetcher, statusText = d3.select("#" + statusId),
8979            layout = d3.layout.cloud().timeInterval(10).size([w, h]).fontSize(function (t)
8980            {
8981              return fontSize(+t.value)
8982            }).text(function (t)
8983            {
8984              return t.key
8985            }).on("word", progress).on("end", draw),
8986            svg = d3.select("#" + visId).append("svg").attr("width", w).attr("height", h),
8987            background = svg.append("g"),
8988            vis = svg.append("g").attr("transform", "translate(" + [w >> 1, h >> 1] + ")");
8989
8990        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)$/,
8991          punctuation = new RegExp("[" + unicodePunctuationRe + "]", "g"),
8992          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,
8993          discard = /^(@|https?:|\/\/)/,
8994          htmlTags = /(<[^>]*?>|<script.*?<\/script>|<style.*?<\/style>|<head.*?><\/head>)/g,
8995          matchTwitter = /^https?:\/\/([^\.]*\.)?twitter\.com/;
8996
8997        var inputText = $inputText.val();
8998        if (inputText) {
8999          parseText(inputText);
9000        }
9001
9002        function flatten(t, e)
9003        {
9004          if ("string" == typeof t) return t;
9005          var n = [];
9006          for (e in t) {
9007            var a = flatten(t[e], e);
9008            a && n.push(a)
9009          }
9010          return n.join(" ")
9011        }
9012
9013        function parseText(t)
9014        {
9015          tags = {};
9016          var e = {},
9017              n = false//d3.select("#per-line").property("checked");
9018          t.split(n ? /\n/g : wordSeparators).forEach(function (t)
9019          {
9020            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))
9021          }), tags = d3.entries(tags).sort(function (t, e)
9022          {
9023            return e.value - t.value
9024          }), tags.forEach(function (t)
9025          {
9026            t.key = e[t.key]
9027          }), generate()
9028        }
9029
9030        function generate()
9031        {
9032          layout
9033            .font(fontName)
9034            .spiral(spiralName)
9035            .rotate(function ()
9036            {
9037              return angles[Math.floor(Math.random() * angles.length)];
9038            }),
9039          fontSize = d3.scaleLog().range([10, 100]), tags.length && fontSize.domain([+tags[tags.length - 1].value || 1, +tags[0].value]),
9040          complete = 0, statusText.style("display", null),
9041          words = [],
9042          layout.stop().words(tags.slice(0, max = Math.min(tags.length, +maxWords))).start()
9043        }
9044
9045        function progress(t)
9046        {
9047          statusText.text(++complete + "/" + max)
9048        }
9049
9050        function draw(t, e)
9051        {
9052          statusText.style("display", "none"),
9053          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;
9054          var n = vis.selectAll("text").data(words, function (t)
9055          {
9056            return t.text.toLowerCase()
9057          });
9058          n.transition().duration(1e3).attr("transform", function (t)
9059          {
9060            return "translate(" + [t.x, t.y] + ")rotate(" + t.rotate + ")"
9061          }).style("font-size", function (t)
9062          {
9063            return t.size + "px"
9064          }),
9065
9066          n.enter().append("text").attr("text-anchor", "middle").attr("transform", function (t)
9067          {
9068            return "translate(" + [t.x, t.y] + ")rotate(" + t.rotate + ")"
9069          }).style("font-size", "1px").transition().duration(1e3).style("font-size", function (t)
9070          {
9071            return t.size + "px"
9072          }).style("font-family", function (t)
9073          {
9074            return t.font
9075          }).text(function (t)
9076          {
9077            return t.text
9078          }).style("fill", function (t)
9079          {
9080              return colors[Math.floor(Math.random() * colors.length)];
9081          });
9082
9083          var a = background.append("g").attr("transform", vis.attr("transform")),
9084              r = a.node();
9085          n.exit().each(function ()
9086          {
9087            r.appendChild(this)
9088          }), a.transition().duration(1e3).style("opacity", 1e-6).remove(), vis.transition().delay(1e3).duration(750).attr("transform", "translate(" + [w >> 1, h >> 1] + ")scale(" + scale + ")")
9089        }
9090
9091      }
9092    }
9093
9094    return exports;
9095  }());
9096}(jQuery));
9097;
9098(function ($) {
9099    'use strict';
9100    window.Rock = window.Rock || {};
9101    Rock.controls = Rock.controls || {};
9102
9103    Rock.controls.yearPicker = (function () {
9104        var exports = {
9105            initialize: function (options) {
9106                if (!options.id) {
9107                    throw 'id is required';
9108                }
9109
9110                var earliestDate = new Date(1000, 1, 1);
9111
9112                // uses https://github.com/eternicode/bootstrap-datepicker
9113                $('#' + options.id).datepicker({
9114                    format: 'yyyy',
9115                    assumeNearbyYear: 10,
9116                    autoclose: true,
9117                    startView: 2,
9118                    minViewMode: 2,
9119                    startDate: earliestDate,
9120                    zIndexOffset: 1050
9121                });
9122
9123            }
9124        };
9125
9126        return exports;
9127    }());
9128}(jQuery));
9129;
9130var loadFunction = function () {
9131    // handle what should happen when a different compare type is selected
9132    function updateFilterControls(filterCompareControl) {
9133        var $fieldCriteriaRow = $(filterCompareControl).closest('.field-criteria');
9134        var compareValue = $(filterCompareControl).val();
9135        var isNullCompare = (compareValue == 32 || compareValue == 64);
9136        var isBetweenCompare = (compareValue == 4096);
9137        if (isNullCompare) {
9138            $fieldCriteriaRow.find('.js-filter-control').hide();
9139            $fieldCriteriaRow.find('.js-filter-control-between').hide();
9140        }
9141        else if (isBetweenCompare) {
9142            $fieldCriteriaRow.find('.js-filter-control').hide();
9143            $fieldCriteriaRow.find('.js-filter-control-between').show();
9144        }
9145        else {
9146            $fieldCriteriaRow.find('.js-filter-control').show();
9147            $fieldCriteriaRow.find('.js-filter-control-between').hide();
9148        }
9149    }
9150
9151    $('.js-filter-compare').each(function (e) {
9152        updateFilterControls(this);
9153    });
9154
9155    $('.js-filter-compare').on("change", function () {
9156        updateFilterControls(this);
9157    });
9158
9159    // handle property selection changes from the EntityFieldFilter
9160    $('select.entity-property-selection').on("change", function () {
9161        var $parentRow = $(this).closest('.js-filter-row');
9162        $parentRow.find('div.field-criteria').hide();
9163        $parentRow.find('div.field-criteria').eq($(this).find(':selected').index()).show();
9164    });
9165
9166    // activity animation on filter field cootrol
9167    $('.filter-item > header').on("click", function () {
9168        $(this).siblings('.panel-body').slideToggle();
9169        $(this).children('div.pull-left').children('div').slideToggle();
9170
9171        $expanded = $(this).children('input.filter-expanded');
9172        $expanded.val($expanded.val() == 'True' ? 'False' : 'True');
9173
9174        $('a.filter-view-state > i', this).toggleClass('ti-chevron-down');
9175        $('a.filter-view-state > i', this).toggleClass('ti-chevron-up');
9176    });
9177
9178    // fix so that the Remove button will fire its event, but not the parent event
9179    $('.filter-item a.btn-danger').on("click", function (event) {
9180        event.stopImmediatePropagation();
9181    });
9182
9183    $('.filter-item-select').on("click", function (event) {
9184        event.stopImmediatePropagation();
9185    });
9186}
9187
9188$(document).ready(function () {
9189    loadFunction();
9190    Sys.Application.add_load(loadFunction);
9191});
9192
9193//
9194(function ($) {
9195    'use strict';
9196    window.Rock = window.Rock || {};
9197
9198    Rock.reporting = (function () {
9199        var _reporting = {},
9200            exports = {
9201                //
9202                formatFilterForDateField: function (title, $selectedContent) {
9203                    var betweenMode = $('.js-filter-control-between', $selectedContent).is(':visible');
9204                    var dateValue = '';
9205                    if (betweenMode) {
9206                        return title + ' during ' + $('.js-slidingdaterange-text-value', $selectedContent).val();
9207                    } else {
9208                        var useCurrentDateOffset = $('.js-current-date-checkbox', $selectedContent).is(':checked');
9209
9210                        if (useCurrentDateOffset) {
9211                            var daysOffset = $('.js-current-date-offset', $selectedContent).val();
9212                            if (daysOffset > 0) {
9213                                dateValue = 'Current Date plus ' + daysOffset + ' days';
9214                            }
9215                            else if (daysOffset < 0) {
9216                                dateValue = 'Current Date minus ' + -daysOffset + ' days';
9217                            }
9218                            else {
9219                                dateValue = 'Current Date';
9220                            }
9221                        }
9222                        else {
9223                            dateValue = $('.js-date-picker input', $selectedContent).filter(':visible').val();
9224                        }
9225                        return title + ' ' + $('.js-filter-compare', $selectedContent).find(':selected').text() + ' \'' + dateValue + '\''
9226                    }
9227                },
9228
9229                //
9230                formatFilterForDateTimeField: function (title, $selectedContent) {
9231                    var betweenMode = $('.js-filter-control-between', $selectedContent).is(':visible');
9232                    var dateValue = '';
9233                    var timeValue = '';
9234                    if (betweenMode) {
9235                        return title + ' during ' + $('.js-slidingdaterange-text-value', $selectedContent).val();
9236                    } else {
9237                        var useCurrentDateOffset = $('.js-current-datetime-checkbox', $selectedContent).is(':checked');
9238
9239                        if (useCurrentDateOffset) {
9240                            var minutesOffset = Number($('.js-current-datetime-offset', $selectedContent).val());
9241                            if (minutesOffset > 0) {
9242                                dateValue = 'Current Time plus ' + minutesOffset + ' minutes';
9243                            }
9244                            else if (minutesOffset < 0) {
9245                                dateValue = 'Current Time minus ' + -minutesOffset + ' minutes';
9246                            }
9247                            else {
9248                                dateValue = 'Current Time';
9249                            }
9250                        }
9251                        else {
9252                            dateValue = $('input.js-datetime-date', $selectedContent).filter(':visible').val() || '';
9253                            timeValue = $('input.js-datetime-time', $selectedContent).filter(':visible').val() || '';
9254                        }
9255
9256                        var dateTimeValue = (dateValue + ' ' + timeValue).trim();
9257                        return title + ' ' + $('.js-filter-compare', $selectedContent).find(':selected').text() + ' \'' + dateTimeValue + '\''
9258                    }
9259                },
9260
9261                //
9262                formatFilterForCheckBoxListFilterControl: function (title, $selectedContent) {
9263                    var selectedItems = '';
9264                    $('input:checked', $selectedContent).each(
9265                        function () {
9266                            selectedItems += selectedItems == '' ? '' : ' OR ';
9267                            selectedItems += ' \'' + $(this).parent().text() + '\'';
9268                        });
9269
9270                    return title + ' is ' + selectedItems
9271                },
9272
9273                //
9274                formatFilterForDefinedValueField: function (title, $selectedContent) {
9275                    return formatFilterForCheckBoxListFilterControl(title, $selectedContent);
9276                },
9277
9278                // NOTE: this is specifically for the Rock.Reporting.DataFilter.OtherDataViewFilter (and similar) components
9279                formatFilterForOtherDataViewFilter: function (title, $selectedContent) {
9280                    var dataViewName = $('.js-dataview .js-item-name-value', $selectedContent).val();
9281                    return title + ' ' + dataViewName;
9282                },
9283
9284                //
9285                formatFilterForSelectSingleField: function (title, $selectedContent) {
9286                    return formatFilterForCheckBoxListFilterControl(title, $selectedContent);
9287                },
9288
9289                // NOTE: this is specifically for the Rock.Reporting.DataFilter.Person.InGroupFilter component
9290                formatFilterForGroupFilterField: function (title, $selectedContent) {
9291                    var groupNames = $('.js-group-picker', $selectedContent).find('.selected-names').text();
9292                    var checkedRoles = $('.js-roles', $selectedContent).find(':checked').closest('label');
9293                    var result = title + ' ' + groupNames;
9294                    var includeChildGroups = $('.js-include-child-groups', $selectedContent).is(':checked');
9295                    if (includeChildGroups) {
9296
9297                        var includeDescendantGroups = $('.js-include-child-groups-descendants', $selectedContent).is(':checked');
9298                        var includeSelectedGroups = $('.js-include-selected-groups', $selectedContent).is(':checked');
9299                        var includeInactiveGroups = $('.js-include-inactive-groups', $selectedContent).is(':checked');
9300                        if (includeDescendantGroups) {
9301                            result = result + ' OR descendant groups';
9302                        } else {
9303                            result = result + ' OR child groups';
9304                        }
9305
9306                        if (includeInactiveGroups) {
9307                            result += ", including inactive groups";
9308                        }
9309
9310                        if (!includeSelectedGroups) {
9311                            result = result + ', NOT including selected groups';
9312                        }
9313                    }
9314
9315                    if (checkedRoles.length > 0) {
9316                        var roleCommaList = checkedRoles.map(function () { { return $(this).text() } }).get().join(',');
9317                        result = result + ', with role(s): ' + roleCommaList;
9318                    }
9319
9320                    var groupMemberStatus = $('.js-group-member-status option:selected', $selectedContent).text();
9321                    if (groupMemberStatus) {
9322                        result = result + ', with member status:' + groupMemberStatus;
9323                    }
9324
9325                    var dateAddedDateRangeText = $('.js-dateadded-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val()
9326                    if (dateAddedDateRangeText) {
9327                      result = result + ', added to group in Date Range: ' + dateAddedDateRangeText;
9328                    }
9329
9330                    var firstAttendanceDateRangeText = $('.js-firstattendance-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val()
9331                    if (firstAttendanceDateRangeText) {
9332                      result = result + ', first attendance to group in Date Range: ' + firstAttendanceDateRangeText;
9333                    }
9334
9335                    var lastAttendanceDateRangeText = $('.js-lastattendance-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val()
9336                    if (lastAttendanceDateRangeText) {
9337                      result = result + ', last attendance to group in Date Range: ' + lastAttendanceDateRangeText;
9338                    }
9339
9340                    return result;
9341                },
9342
9343                // NOTE: this is specifically for the Rock.Reporting.DataFilter.Person.HasPhoneFilter component
9344                formatFilterForHasPhoneFilter: function ($content) {
9345
9346                    var has;
9347                    if ($('.js-hasphoneoftype', $content).find(':selected').val() == "True") {
9348                        has = "Has ";
9349                    } else {
9350                        has = "Doesn't have ";
9351                    }
9352
9353                    var phoneType = $('.js-phonetype', $content).find(':selected').text();
9354                    var sms = $('.js-hassms', $content).find(':selected').text();
9355
9356                    if (sms == 'Yes') {
9357                        sms = ' AND has SMS Enabled';
9358                    }
9359                    else if (sms == 'No') {
9360                        sms = " AND doesn't have SMS Enabled";
9361                    }
9362
9363                    var result = has + phoneType + sms;
9364
9365                    return result;
9366                },
9367
9368                //
9369                formatFilterDefault: function (title, $selectedContent) {
9370                    var compareTypeText = $('.js-filter-compare', $selectedContent).find(':selected').text();
9371
9372                    var compareValueText = $('input[type=text].js-filter-control', $selectedContent).val(); // textbox value.
9373                    if (!compareValueText || compareValueText == "") {
9374                        compareValueText = $('.js-filter-control', $selectedContent).find(':selected').map(function () { return this.text; }).get().join("', '");
9375                    }
9376                    if (!compareValueText || compareValueText == "") {
9377                        compareValueText = $('.js-filter-control', $selectedContent).find(':checked').next().map(function () { return $(this).text(); }).get().join("', '");
9378                    }
9379
9380                    var result = title;
9381                    if ($('.js-filter-control', $selectedContent).is(':visible')) {
9382                        result = title + ' ' + compareTypeText + " '" + compareValueText + "'";
9383                    } else {
9384                        result = title + ' ' + compareTypeText;
9385                    }
9386
9387                    return result;
9388                }
9389            };
9390
9391        return exports;
9392    }());
9393}(jQuery));
9394;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.