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 Rock.controls.captcha = (function () { 1975 1976 // #region Constants 1977 1978 const CaptchaMode = Object.freeze({ 1979 Visible: 0, 1980 Invisible: 1, 1981 Disabled: 2 1982 }); 1983 1984 /** 1985 * A map of RockCaptcha controls keyed by their control IDs. 1986 * @type {Object.<string, RockCaptcha>} 1987 */ 1988 const captchaControls = {}; 1989 1990 // #endregion Constants 1991 1992 // #region Types 1993 1994 /** 1995 * @typedef CaptchaConfigurationBag 1996 * @property {CaptchaMode} captchaMode 1997 */ 1998 1999 /** 2000 * @typedef CaptchaInitializeResultBag 2001 * @property {(CaptchaProofOfWorkChallengeBag | null | undefined)} pow Might be null 2002 */ 2003 2004 /** 2005 * @typedef CaptchaProofOfWorkChallengeBag 2006 * @property {number} challengeCount 2007 * @property {number} challengeDifficulty 2008 * @property {number} challengeSize 2009 * @property {string} challengeToken 2010 */ 2011 2012 /** 2013 * @typedef CaptchaVerifyOptionsBag 2014 * @property {CaptchaProofOfWorkVerifyOptionsBag} powOptions Might be null 2015 */ 2016 2017 /** 2018 * @typedef CaptchaProofOfWorkVerifyOptionsBag 2019 * @property {string} challengeToken 2020 * @property {number[]} challengeSolutions 2021 */ 2022 2023 /** 2024 * @typedef CaptchaVerifyResultBag 2025 * @property {boolean} isVerified 2026 * @property {string} error Can be null 2027 * @property {number} expires Can be null 2028 * @property {string} token Can be null 2029 */ 2030 2031 /** 2032 * @typedef InitializeConfig 2033 * @property {string} id The ID for the CAPTCHA control. 2034 * @property {string} postBackScript The postback script when a CAPTCHA token has been verified. 2035 */ 2036 2037 /** 2038 * The RockCaptcha initialization options. 2039 * @typedef RockCaptchaInitOptions 2040 * @property {string} controlId The control ID for the element (without leading #). 2041 * @property {number} captchaMode The CAPTCHA mode. 2042 * @property {string} postBackScript The script to run when the CAPTCHA token is resolved. 2043 */ 2044 2045 /** 2046 * The Api initialization options. 2047 * @typedef ApiInitOptions 2048 * @property {string} baseUrl The base URL for API requests. 2049 */ 2050 2051 /** 2052 * @typedef SolveChallengeOptions 2053 * @property {string[]} solutions 2054 * @property {string} token 2055 */ 2056 2057 /** 2058 * @typedef CapChallengeResult 2059 * @property {CapChallenge} challenge Might be null 2060 * @property {string} token Might be null 2061 */ 2062 2063 /** 2064 * @typedef CapChallenge 2065 * @property {number} c 2066 * @property {number} s 2067 * @property {number} d 2068 */ 2069 2070 /** 2071 * @typedef CapRedeemOptions 2072 * @property {number[]} solutions 2073 * @property {string} token 2074 */ 2075 2076 /** 2077 * @typedef CapRedeemResult 2078 * @property {boolean} success 2079 * @property {string} message Might be null 2080 * @property {number} expires Might be null 2081 * @property {string} token Might be null 2082 */ 2083 2084 /** 2085 * Creates a new instance of the RockCaptcha class. 2086 * @param {RockCaptchaInitOptions} options 2087 */ 2088 function RockCaptcha(options) { 2089 if (!options.controlId) { 2090 throw "`controlId` is required."; 2091 } 2092 2093 // Set properties. 2094 /** @type {string} */ 2095 this.controlId = options.controlId; 2096 2097 /** @type {CaptchaMode} */ 2098 this.captchaMode = options.captchaMode; 2099 2100 /** @type {string} */ 2101 this.postBackScript = options.postBackScript; 2102 2103 /** @type {string} */ 2104 this.captchaToken = ""; // initialize with an empty CATCHA token. 2105 2106 /** @type {HTMLElement | null} Can be null */ 2107 this.widgetEl = null; // initialize the widget element to null. 2108 2109 /** @type {boolean} */ 2110 this.disposed = false; 2111 2112 // Set JQuery selectors. 2113 /** @type {JQuery<HTMLElement>} */ 2114 this.$controlSelector = $("#" + this.controlId); 2115 2116 /** @type {JQuery<HTMLElement>} */ 2117 this.$hfToken = $("#" + this.controlId + '_hfToken'); 2118 } 2119 2120 RockCaptcha.prototype.render = async function () { 2121 const areCustomElementsSupported = typeof window.customElements !== "undefined"; 2122 2123 if (areCustomElementsSupported && this.captchaMode === CaptchaMode.Visible) { 2124 this.widgetEl = document.createElement("cap-widget"); 2125 this.widgetEl.setAttribute("data-cap-api-endpoint", "/"); // This is a required attribute for the library but the endpoint will be overridden later. 2126 2127 // Styles 2128 this.widgetEl.style.setProperty("--cap-background", "var(--color-interface-softest)"); 2129 this.widgetEl.style.setProperty("--cap-border-radius", "var(--rounded-small)"); 2130 this.widgetEl.style.setProperty("--cap-border-color", "var(--color-interface-soft)"); 2131 this.widgetEl.style.setProperty("--cap-widget-padding", "var(--spacing-medium)"); 2132 this.widgetEl.style.setProperty("--cap-color", "var(--color-interface-strong)"); 2133 this.widgetEl.style.setProperty("--cap-widget-width", "200px"); 2134 this.widgetEl.style.setProperty("--cap-checkbox-background", "var(--color-interface-softest)"); 2135 this.widgetEl.style.setProperty("--cap-checkbox-border", "1px solid var(--color-interface-soft)"); 2136 this.widgetEl.style.setProperty("--cap-checkbox-border-radius", "var(--rounded-xsmall)"); 2137 this.widgetEl.style.setProperty("--cap-checkbox-margin", "var(--spacing-large)"); 2138 this.widgetEl.style.setProperty("--cap-spinner-background-color", "var(--color-interface-soft)"); 2139 this.widgetEl.style.setProperty("--cap-spinner-color", "var(--color-primary)"); 2140 this.widgetEl.style.setProperty("--cap-spinner-thickness", "6px"); 2141 this.widgetEl.style.setProperty("--cap-gap", "var(--spacing-medium)"); 2142 this.widgetEl.style.setProperty("--cap-font", "var(--font-family-body)"); 2143 this.widgetEl.style.setProperty("--cap-checkbox-size", "20px"); 2144 2145 this.widgetEl.addEventListener("solve", async (e) => { 2146 if (this.disposed) { 2147 return; 2148 } 2149 2150 const token = e && e.detail && e.detail.token ? e.detail.token : undefined; 2151 2152 this.handleSolvedToken(token); 2153 }); 2154 2155 this.widgetEl.addEventListener("error", (_e) => { 2156 if (this.disposed) { 2157 return; 2158 } 2159 2160 this.handleError("Captcha challenge failed."); 2161 }); 2162 2163 const host = this.$controlSelector[0];
2164 if (host) { 2165 host.appendChild(this.widgetEl); 2166 2167 setTimeout(() => { 2168 if (this.widgetEl && this.widgetEl.shadowRoot) { 2169 const attribution = this.widgetEl.shadowRoot.querySelector('[part="attribution"]'); 2170 if (attribution) { 2171 attribution.remove(); 2172 } 2173 } 2174 }) 2175 } 2176 2177 return; 2178 } 2179 2180 // Invisible mode fallback (older Safari/Edge without customElements) 2181 try { 2182 const cap = new Cap(); 2183 const token = await cap.solve(); 2184 if (!this.disposed) { 2185 await this.handleSolvedToken(token.token); 2186 } 2187 } 2188 catch { 2189 if (!this.disposed) { 2190 this.handleError("Captcha challenge failed."); 2191 } 2192 } 2193 }; 2194 2195 /** 2196 * Handles processing a CAPTCHA token after it is solved. 2197 * @param {string} token 2198 */ 2199 RockCaptcha.prototype.handleSolvedToken = async function (token) { 2200 this.captchaToken = token; 2201 this.$hfToken.val(token); 2202 2203 // Notify the page that the CAPTCHA has been solved so client code can 2204 // react (e.g., reveal a Next button) without needing a server postback. 2205 // Fires for both Visible and Invisible modes. 2206 this.$controlSelector.trigger('rockcaptcha:solved', [{ token: token }]); 2207 2208 if (this.postBackScript) { 2209 window.location = "javascript:" + this.postBackScript; 2210 } 2211 }; 2212 2213 /** 2214 * Handles an error. 2215 * @param {string} msg 2216 */ 2217 RockCaptcha.prototype.handleError = function (msg) { 2218 console.error(msg); 2219 this.captchaToken = ""; 2220 }; 2221 2222 /** 2223 * Creates a new instance of the Api class. 2224 * @param {ApiInitOptions} options 2225 */ 2226 function Api(options) { 2227 this.baseUrl = options.baseUrl; 2228 } 2229 2230 /** 2231 * Gets the CAPTCHA configuration. 2232 * @returns {Promise<CaptchaConfigurationBag>} 2233 */ 2234 Api.prototype.getConfiguration = async function () { 2235 return await Promise.resolve($.post({ 2236 url: this.baseUrl + 'api/v2/Controls/CaptchaGetConfiguration' 2237 })); 2238 }; 2239 2240 /** 2241 * Initializes the CAPTCHA. 2242 * @returns {Promise<CaptchaInitializeResultBag>} 2243 */ 2244 Api.prototype.initialize = async function () { 2245 return await Promise.resolve($.post({ 2246 url: this.baseUrl + 'api/v2/Controls/CaptchaInitialize' 2247 })); 2248 }; 2249 2250 /** 2251 * Verifies the CAPTCHA. 2252 * @param {CaptchaVerifyOptionsBag} options 2253 * @returns {Promise<CaptchaVerifyResultBag>} 2254 */ 2255 Api.prototype.verify = async function (options) { 2256 return await Promise.resolve($.post({ 2257 url: this.baseUrl + 'api/v2/Controls/CaptchaVerify', 2258 data: options 2259 })); 2260 }; 2261 2262 // #endregion Types 2263 2264 /** 2265 * Gets whether the Cap module is ready. 2266 * 2267 * @returns Whether the Cap module is ready. 2268 */ 2269 function isCapReady() { 2270 return typeof Cap !== 'undefined'; 2271 } 2272 2273 const api = new Api({ 2274 baseUrl: '/' 2275 }); 2276 2277 async function loadCap() { 2278 if (typeof window.Cap !== "undefined") { 2279 return; 2280 } 2281 else { 2282 $.ajaxSetup({ cache: true }); 2283 return await Promise.resolve($.getScript("https://cdn.jsdelivr.net/npm/@cap.js/[email protected]")); 2284 } 2285 } 2286 2287 var exports = { 2288 /** 2289 * Initializes a new RockCaptcha instance. 2290 * @param {InitializeConfig} config 2291 */ 2292 initialize: async function (config) { 2293 if (!config) { 2294 return; 2295 } 2296 2297 if (!config.id) { 2298 throw 'id is required'; 2299 } 2300 2301 function freezeProp(target, propertyName, value) { 2302 Object.defineProperty(target, propertyName, { 2303 get: () => value, 2304 set: undefined, 2305 configurable: false, 2306 enumerable: true 2307 }); 2308 } 2309 2310 // Override the fetch function used by Cap to route requests via the Rock captcha API. 2311 if (!window.CAP_CUSTOM_FETCH) { 2312 freezeProp(window, "CAP_CUSTOM_FETCH", async function (url, options) { 2313 if (typeof url === "string") { 2314 if (url.endsWith("/challenge")) { 2315 const result = await api.initialize(); 2316 2317 // Format the challenge as expected by Cap.js 2318 2319 /** @type {CapChallengeResult} */ 2320 const capResult = { 2321 challenge: null, 2322 token: null 2323 }; 2324 2325 if (result && result.pow) { 2326 capResult.token = result.pow.challengeToken; 2327 capResult.challenge = { 2328 c: result.pow.challengeCount, 2329 s: result.pow.challengeSize, 2330 d: result.pow.challengeDifficulty 2331 }; 2332 } 2333 2334 return Response.json(capResult); 2335 } 2336 else if (url.endsWith("/redeem")) { 2337 /** @type {CapRedeemOptions} */ 2338 const capRedeemOptions = options && options.body ? JSON.parse(options.body.toString()) : null; 2339 2340 /** @type {CaptchaVerifyOptionsBag} */ 2341 const verifyOptions = { 2342 powOptions: null 2343 }; 2344 2345 if (capRedeemOptions) { 2346 verifyOptions.powOptions = { 2347 challengeSolutions: capRedeemOptions.solutions, 2348 challengeToken: capRedeemOptions.token 2349 }; 2350 } 2351 2352 const result = await api.verify(verifyOptions); 2353 2354 /** @type {CapRedeemResult} */ 2355 const capResult = { 2356 success: false, 2357 expires: null, 2358 message: "An error occurred", 2359 token: null 2360 }; 2361 2362 if (result) { 2363 capResult.success = result.isVerified; 2364 capResult.message = result.error; 2365 capResult.expires = result.expires; 2366 capResult.token = result.token; 2367 }; 2368 2369 return Response.json(capResult); 2370 } 2371 } 2372 2373 return fetch(url, options); 2374 }); 2375 } 2376
2377 const rockCaptcha = captchaControls[config.id]; 2378 if (rockCaptcha) { 2379 if (rockCaptcha.widgetEl) { 2380 rockCaptcha.widgetEl.remove(); 2381 } 2382 } 2383 2384 await loadCap(); 2385 const captchaConfig = await api.getConfiguration(); 2386 2387 // Store a ref to the new control so it can be managed later. 2388 captchaControls[config.id] = new RockCaptcha({ 2389 api: api, 2390 captchaMode: captchaConfig.captchaMode, 2391 controlId: config.id, 2392 postBackScript: config.postBackScript 2393 }); 2394 2395 $(document).ready(async function () { 2396 await captchaControls[config.id].render(); 2397 }); 2398 }, 2399 clientValidate: function (validator, args) { 2400 var $validator = $(validator); 2401 var isRequired = $validator.data('required'); 2402 2403 var rockCaptchaId = $validator.data('captcha-id'); 2404 var $formGroup = $('#' + rockCaptchaId).closest('.form-group'); 2405 2406 if (isRequired && isCapReady()) { 2407 // Get this validator control's related Rock Captcha control ID and 2408 // try to find the underlying Turnstile widget. 2409 var turnstileId = captchaIds[rockCaptchaId]; 2410 if (turnstileId) { 2411 var turnstileResponse = turnstile.getResponse(turnstileId); 2412 } 2413 } 2414 2415 var isValid = !!(!isRequired || turnstileResponse); 2416 2417 if (isValid) { 2418 args.IsValid = true; 2419 $formGroup.removeClass('has-error'); 2420 } 2421 else { 2422 args.IsValid = false; 2423 $formGroup.addClass('has-error'); 2424 } 2425 } 2426 }; 2427 2428 return exports; 2429 }()); 2430}(jQuery)); 2431; 2432(function ($) { 2433 'use strict'; 2434 window.Rock = window.Rock || {}; 2435 2436 Rock.controls.charts = (function () { 2437 var exports = { 2438 2439 /// 2440 /// handles putting chartData into a Line/Bar/Points chart 2441 /// 2442 plotChartData: function (chartData, chartOptions, plotSelector, yaxisLabelText, getSeriesPartitionNameUrl, combineValues) { 2443 2444 var chartSeriesLookup = {}; 2445 var chartSeriesList = []; 2446 2447 var chartGoalPoints = { 2448 label: yaxisLabelText + ' Goal', 2449 chartData: [], 2450 data: [] 2451 } 2452 2453 if (chartOptions.customSettings) { 2454 chartGoalPoints.color = chartOptions.customSettings.goalSeriesColor; 2455 } 2456 2457 // populate the chartMeasurePoints data array with data from the REST result 2458 for (var i = 0; i < chartData.length; i++) { 2459 if (chartData[i].MetricValueType && chartData[i].MetricValueType == 1) { 2460 // if the chartdata is marked as a Metric Goal Value Type, populate the chartGoalPoints 2461 chartGoalPoints.data.push([chartData[i].DateTimeStamp, chartData[i].YValue]); 2462 chartGoalPoints.chartData.push(chartData[i]); 2463 } 2464 else { 2465 var lookupKey = chartData[i].MetricValuePartitionEntityIds; 2466 if (!lookupKey || lookupKey == '') { 2467 lookupKey = chartData[i].SeriesName; 2468 } 2469 2470 if (!chartSeriesLookup[lookupKey]) { 2471 2472 // If SeriesName is specified, that can be the name of the series if MetricValuePartitionEntityIds is blank 2473 var seriesName = chartData[i].SeriesName; 2474 2475 // if we aren't combining values, we'll have to lookup the series n
2475ame based on MetricValuePartitionEntityIds 2476 if (chartData[i].MetricValuePartitionEntityIds && chartData[i].MetricValuePartitionEntityIds != '' && !combineValues) 2477 { 2478 // MetricValuePartitionEntityIds is not blank so get the seriesName from the getSeriesPartitionNameUrl 2479 if (getSeriesPartitionNameUrl) { 2480 var metricValuePartitionEntityIdDataJSON = JSON.stringify(chartData[i].MetricValuePartitionEntityIds.split(',')); 2481 2482 $.ajax({ 2483 type: 'POST', 2484 url: getSeriesPartitionNameUrl, 2485 data: metricValuePartitionEntityIdDataJSON, 2486 async: false, 2487 success: null, 2488 contentType: 'application/json' 2489 }) 2490 .done(function (data) { 2491 seriesName = data; 2492 }); 2493 } 2494 } 2495 2496 // either either seriesName, yaxisLabelText or just 'value' if the seriesname isn't defined 2497 seriesName = seriesName || yaxisLabelText || 'value'; 2498 2499 chartSeriesLookup[lookupKey] = { 2500 label: seriesName, 2501 chartData: [], 2502 data: [] 2503 }; 2504 } 2505 2506 chartSeriesLookup[lookupKey].data.push([chartData[i].DateTimeStamp, chartData[i].YValue]); 2507 chartSeriesLookup[lookupKey].chartData.push(chartData[i]); 2508 } 2509 } 2510 2511 // setup the series list (goal points last, if they exist) 2512 for (var chartSeriesKey in chartSeriesLookup) { 2513 var chartMeasurePoints = chartSeriesLookup[chartSeriesKey]; 2514 if (chartMeasurePoints.data.length) { 2515 chartSeriesList.push(chartMeasurePoints); 2516 } 2517 } 2518 2519 if (combineValues && chartSeriesList.length != 1) { 2520 2521 var combinedDataLookup = {}; 2522 var chartMeasurePoints = null; 2523 2524 for (var chartMeasurePointsId in chartSeriesList) { 2525 2526 chartMeasurePoints = chartSeriesList[chartMeasurePointsId]; 2527 2528 $.each(chartMeasurePoints.data, function (indexInArray, dataPair) { 2529 var dateTimeIndex = dataPair[0]; 2530 var combinedItem = combinedDataLookup[dateTimeIndex]; 2531 if (combinedItem) { 2532 // sum the YValue of the .data 2533 combinedItem.chartData.YValue += chartMeasurePoints.chartData[indexInArray].YValue; 2534 combinedItem.data[1] += dataPair[1]; 2535 } 2536 else { 2537 combinedDataLookup[dateTimeIndex] = { 2538 chartData: chartMeasurePoints.chartData[indexInArray], 2539 data: dataPair 2540 }; 2541 } 2542 }); 2543 } 2544 2545 var combinedData = []; 2546 var combinedChartData = []; 2547 2548 for (var dataLookupId in combinedDataLookup) { 2549 combinedData.push(combinedDataLookup[dataLookupId].data); 2550 combinedChartData.push(combinedDataLookup[dataLookupId].chartData); 2551 } 2552 2553 chartSeriesList = []; 2554 2555 // sort data after combining 2556 combinedChartData.sort(function (item1, item2) { return item2.DateTimeStamp - item1.DateTimeStamp }); 2557 combinedData.sort(function (item1, item2) { return item2[0] - item1[0]; }); 2558 2559 var chartCombinedMeasurePoints = { 2560 label: yaxisLabelText + ' Total', 2561 chartData: combinedChartData, 2562 data: combinedData 2563 }; 2564 2565 2566 chartSeriesList.push(chartCombinedMeasurePoints); 2567 } 2568 2569 if (chartGoalPoints.data.length) { 2570 chartSeriesList.push(chartGoalPoints); 2571 } 2572 2573 // plot the chart 2574 if (chartSeriesList.length > 0) { 2575 $.plot(plotSelector, chartSeriesList, chartOptions); 2576 } 2577 else { 2578 $(plotSelector).html('<div class="alert alert-info">No Data Found</div>'); 2579 } 2580 }, 2581 2582 /// 2583 /// handles putting chart data into a piechart 2584 /// 2585 plotPieChartData: function (chartData, chartOptions, plotSelector, getSeriesPartitionNameUrl) { 2586 var pieData = []; 2587 2588 // populate the chartMeasurePoints data array with data from the REST result for pie data 2589 for (var i = 0; i < chartData.length; i++) { 2590 2591 pieData.push({ 2592 label: chartData[i].MetricTitle,
2593 data: chartData[i].YValueTotal, 2594 chartData: [chartData[i]] 2595 }); 2596 } 2597 2598 if (pieData.length > 0) { 2599 // plot the pie chart 2600 $.plot(plotSelector, pieData, chartOptions); 2601 } 2602 else { 2603 $(plotSelector).html('<div class="alert alert-info">No Data Found</div>'); 2604 } 2605 }, 2606 2607 /// 2608 /// handles putting chart data into a bar chart 2609 /// 2610 plotBarChartData: function (chartData, chartOptions, plotSelector, getSeriesPartitionNameUrl) { 2611 var barData = []; 2612 var combinedData = {}; 2613 var seriesLabels = []; 2614 var seriesNameLookup = {}; 2615 2616 // populate the chartMeasurePoints data array with data from the REST result for bar chart data 2617 for (var i = 0; i < chartData.length; i++) { 2618 2619 var seriesCategory = chartData[i].SeriesName; 2620 if (chartData[i].MetricValuePartitionEntityIds) 2621 { 2622 if (!seriesNameLookup[chartData[i].MetricValuePartitionEntityIds]) 2623 { 2624 // MetricValuePartitionEntityIds is not blank so get the seriesName from the getSeriesPartitionNameUrl 2625 if (getSeriesPartitionNameUrl) { 2626 var metricValuePartitionEntityIdDataJSON = JSON.stringify(chartData[i].MetricValuePartitionEntityIds.split(',')); 2627 2628 $.ajax({
2629 type: 'POST', 2630 url: getSeriesPartitionNameUrl, 2631 data: metricValuePartitionEntityIdDataJSON, 2632 async: false, 2633 success: null, 2634 contentType: 'application/json' 2635 }) 2636 .done(function (data) { 2637 seriesNameLookup[chartData[i].MetricValuePartitionEntityIds] = data; 2638 }); 2639 } 2640 } 2641 2642 seriesCategory = seriesNameLookup[chartData[i].MetricValuePartitionEntityIds] || seriesCategory; 2643 } 2644 2645 if (!combinedData[seriesCategory]) 2646 { 2647 combinedData[seriesCategory] = 0; 2648 } 2649 2650 combinedData[seriesCategory] += chartData[i].YValue; 2651 } 2652 2653 var seriesId = 0; 2654 for (var combinedDataKey in combinedData) { 2655 2656 barData.push([combinedDataKey, combinedData[combinedDataKey]]); 2657 seriesId++; 2658 seriesLabels.push(combinedDataKey); 2659 } 2660 2661 seriesLabels.push(seriesCategory); 2662 2663 if (barData.length > 0) { 2664 // plot the bar chart 2665 chartOptions.series.chartData = chartData; 2666 chartOptions.series.labels = seriesLabels; 2667 $.plot(plotSelector, [barData], chartOptions); 2668 } 2669 else { 2670 $(plotSelector).html('<div class="alert alert-info">No Data Found</div>'); 2671 } 2672 }, 2673 2674 /// 2675 /// attaches a bootstrap style tooltip to the 'plothover' of a chart 2676 /// 2677 bindTooltip: function (plotContainerId, tooltipFormatter) { 2678 // setup of bootstrap tooltip which we'll show on the plothover event 2679 var toolTipId = 'tooltip_' + plotContainerId; 2680 var chartContainer = '#' + plotContainerId; 2681 2682 $("<div id=" + toolTipId + " class='tooltip top'><div class='tooltip-inner'></div><div class='tooltip-arrow'></div></div>").css({ 2683 position: 'absolute', 2684 display: 'none', 2685 }).appendTo('body'); 2686 2687 var $toolTip = $('#' + toolTipId); 2688 2689 $(chartContainer).bind('plothover', function (event, pos, item) { 2690 2691 if (item) { 2692 2693 var tooltipText = ""; 2694 2695 // if a tooltipFormatter is specified, use that 2696 if (tooltipFormatter) { 2697 tooltipText = tooltipFormatter(item); 2698 } 2699 else { 2700 if (item.series.chartData) { 2701 if (item.series.chartData[item.dataIndex].DateTimeStamp) { 2702 tooltipText = new Date(item.series.chartData[item.dataIndex].DateTimeStamp).toLocaleDateString(); 2703 }; 2704 2705 if (item.series.chartData[item.dataIndex].StartDateTimeStamp) { 2706 tooltipText = new Date(item.series.chartData[item.dataIndex].StartDateTimeStamp).toLocaleDateString(); 2707 } 2708 2709 if (item.series.chartData[item.dataIndex].EndDateTimeStamp) { 2710 tooltipText += " to " + new Date(item.series.chartData[item.dataIndex].EndDateTimeStamp).toLocaleDateString(); 2711 } 2712 2713 if (item.series.chartData[item.dataIndex].MetricTitle) { 2714 tooltipText = item.series.chartData[item.dataIndex].MetricTitle; 2715 } 2716 } 2717 2718 if (tooltipText) { 2719 tooltipText += '<br />'; 2720 } 2721 2722 if (item.series.label) { 2723 tooltipText += item.series.label; 2724 } 2725 2726 if (item.series.chartData) { 2727 var pointValue = item.series.chartData[item.dataIndex].YValueFormatted || item.series.chartData[item.dataIndex].YValue || item.series.chartData[item.dataIndex].YValueTotal || ''; 2728 2729 tooltipText += ': ' + pointValue; 2730 2731 if (item.series.chartData[item.dataIndex].Note) { 2732 tooltipText += '<br />' + item.series.chartData[item.dataIndex].Note; 2733 } 2734 } 2735 } 2736 2737 $toolTip.find('.tooltip-inner').html(tooltipText); 2738 2739 var tipTop = pos.pageY - $toolTip.height() - 10; 2740 2741 var windowWidth = $(window).width(); 2742 var tooltipWidth = $toolTip.width();
2743 var tipLeft = pos.pageX - ( tooltipWidth / 2); 2744 if (tipLeft + tooltipWidth + 10 >= windowWidth) { 2745 tipLeft = tipLeft - (tooltipWidth / 2); 2746 $toolTip.removeClass("top"); 2747 $toolTip.addClass("left"); 2748 } 2749 else { 2750 $toolTip.removeClass("left"); 2751 $toolTip.addClass("top"); 2752 } 2753 2754 $toolTip.css({ top: tipTop, left: tipLeft, opacity: 1 }); 2755 $toolTip.show(); 2756 } 2757 else { 2758 $toolTip.hide(); 2759 } 2760 }); 2761 2762 } 2763 }; 2764 2765 return exports; 2766 }()); 2767}(jQuery)); 2768 2769 2770; 2771(function ($) { 2772 'use strict'; 2773 window.Rock = window.Rock || {}; 2774 Rock.controls = Rock.controls || {}; 2775 2776 Rock.controls.colorSelector = (function () { 2777 2778 // #region Utilities 2779 2780 var ColorUtilities = (function () { 2781 /** 2782 * Detects if two RGB colors are similar. 2783 * @param r1 {number} Red 1 color (0-255). 2784 * @param g1 {number} Green 1 color (0-255). 2785 * @param b1 {number} Blue 1 color (0-255). 2786 * @param r2 {number} Red 2 color (0-255). 2787 * @param g2 {number} Green 2 color (0-255). 2788 * @param b2 {number} Blue 2 color (0-255). 2789 * @param percentSimilar {number|undefined} The percent (0 - 1) that determines if the colors are similar. 2790 * @returns {Boolean} `true` if the numbers are similar; otherwise, `false`. 2791 */ 2792 function areColorsSimilar(r1, g1, b1, r2, g2, b2, percentSimilar) { 2793 if (typeof percentSimilar === "undefined") { 2794 percentSimilar = 0.99; 2795 } 2796 2797 var actualDistance = getColorDistance(r1, g1, b1, r2, g2, b2); 2798 2799 var actualPercent = (maxLumaDistance - actualDistance) / maxLumaDistance; 2800 2801 // Use the specified percent to determine if the colors are similar. 2802 return actualPercent >= percentSimilar; 2803 } 2804 2805 /** 2806 * Gets the distance between two RGB colors. 2807 * @param r1 {number} Red 1 color (0-255). 2808 * @param g1 {number} Green 1 color (0-255). 2809 * @param b1 {number} Blue 1 color (0-255). 2810 * @param r2 {number} Red 2 color (0-255). 2811 * @param g2 {number} Green 2 color (0-255). 2812 * @param b2 {number} Blue 2 color (0-255). 2813 * @returns {number} The distance between two RGB colors. 2814 */ 2815 function getColorDistance(r1, g1, b1, r2, g2, b2) { 2816 var redDiffSquared = Math.pow(r2 - r1, 2); 2817 var greenDiffSquared = Math.pow(g2 - g1, 2); 2818 var blueDiffSquared = Math.pow(b2 - b1, 2); 2819 var redAverage = (r2 + r1) / 2; 2820 2821 return Math.sqrt( 2822 (2 * redDiffSquared) + 2823 (4 * greenDiffSquared) + 2824 (3 * blueDiffSquared) + 2825 (redAverage * (redDiffSquared - blueDiffSquared) / 256) 2826 ); 2827 } 2828 2829 /** 2830 * Converts a hex color (#FFFFFF) to an RGB color. 2831 * @param {string} hexValue The hex color to convert. 2832 * @returns {Number[]} The converted RGB color. 2833 */ 2834 function hexToRgba(hexValue) { 2835 var hex3Or4 = /^#[0-9a-fA-F]{3}([0-9a-fA-F])?$/; 2836 if (hex3Or4.test(hexValue)) { 2837 var rHex = hexValue[1]; 2838 var gHex = hexValue[2]; 2839 var bHex = hexValue[3]; 2840 var aHex = hexValue[4] || "f"; 2841 // A 3-digit hex color #123 is equivalent to the 6-digit hex color #112233, 2842 // where each color channel (r, g, and b) is repeated. 2843 return [ 2844 parseInt(rHex + rHex, 16), 2845 parseInt(gHex + gHex, 16), 2846 parseInt(bHex + bHex, 16), 2847 parseInt(aHex + aHex, 16) 2848 ]; 2849 } 2850 2851 var hex6Or8 = /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/; 2852 if (hex6Or8.test(hexValue)) { 2853 var rHex = hexValue.substring(1, 3); 2854 var gHex = hexValue.substring(3, 5); 2855 var bHex = hexValue.substring(5, 7); 2856 var aHex = hexValue.substring(7) || "ff"; 2857 return [ 2858 parseInt(rHex, 16), 2859 parseInt(gHex, 16), 2860 parseInt(bHex, 16),
2861 Math.round(parseInt(aHex, 16) / 255) 2862 ]; 2863 } 2864 2865 // Return transparent if not a valid hex color. 2866 return [0, 0, 0, 0]; 2867 } 2868 2869 /** 2870 * Returns the perceived brightness of an RGB color. 2871 * @param r {number} Red color (0-255). 2872 * @param g {number} Green color (0-255). 2873 * @param b {number} Blue color (0-255). 2874 * @returns {Number[]} 2875 */ 2876 function rgbToLuma(r, g, b) { 2877 /** 2878 * To get a color's perceived brightness, 2879 * each color component is multiplied by a different constant. 2880 * The constants are weighted differently based on the perceived brightness by the human eye 2881 * --green is perceived as the brightest color, then red, then blue (they all add up to 1). 2882 * 2883 * The result will be a color in the color space: 2884 * [0, 0, 0] to [0.2126 * 255, 0.7152 * 255, 0.0722 * 255] 2885 * or... 2886 * [0, 0, 0] to [54.213, 182.376, 18.411] 2887 */ 2888 2889 return [0.2126 * r, 0.7152 * g, 0.0722 * b]; 2890 } 2891 2892 /** 2893 * Returns the RGBA number array from a CSS "rgba(r, g, b, a)" string. 2894 * @param rgbFunc {string} The "rgb(r, g, b)" string. 2895 * @returns {Number[]} An RGBA array [r, b, b, a]. 2896 */ 2897 function rgbFuncToRgba(rgbFunc) { 2898 var rgb = /^rgb[a]?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)\)$/; 2899 var rgbMatches = rgbFunc.match(rgb); 2900 if (rgbMatches && rgbMatches.length) { 2901 return [ 2902 parseInt(rgbMatches[1]), 2903 parseInt(rgbMatches[2]), 2904 parseInt(rgbMatches[3]), 2905 1 2906 ]; 2907 } 2908 2909 var rgba = /^rgb[a]?\s*\(\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d+)[, ]\s*(\d*(\.\d+)?)\)$/; 2910 var rgbaMatches = rgbFunc.match(rgba); 2911 if (rgbaMatches && rgbMatches.length) { 2912 return [ 2913 parseInt(rgbaMatches[1]), 2914 parseInt(rgbaMatches[2]), 2915 parseInt(rgbaMatches[3]), 2916 Number(rgbaMatches[4]) 2917 ]; 2918 } 2919 2920 // Return transparent by default. 2921 return [0, 0, 0, 0]; 2922 } 2923 2924 return { 2925 areColorsSimilar, 2926 getColorDistance, 2927 hexToRgba, 2928 rgbToLuma, 2929 rgbFuncToRgba 2930 } 2931 })(); 2932 2933 // #endregion 2934 2935 // #region Types 2936 2937 /** 2938 * The ColorSelector initialization options. 2939 * @typedef {Object} ColorSelectorInitOptions 2940 * @property {string} controlId The control ID for the color selector (without leading #). 2941 * @property {boolean} allowMultiple When true, the Color Selector will behave like a checkbox list; otherwise, it will behave like a radiobutton list. 2942 */ 2943 2944 // #endregion 2945 2946 // #region Constants 2947 2948 var colorSimilarityPercentage = Object.freeze(0.95); 2949 var maxLuma = Object.freeze(ColorUtilities.rgbToLuma(255, 255, 255)); 2950 var maxLumaDistance = Object.freeze(ColorUtilities.getColorDistance(maxLuma[0], maxLuma[1], maxLuma[2], 0, 0, 0)); 2951 var camouflagedLightClass = Object.freeze("camouflaged-light"); 2952 var camouflagedDarkClass = Object.freeze("camouflaged-dark"); 2953 2954 // #endregion 2955 2956 // #region Classes 2957 2958 // #region Constructor 2959 2960 /** 2961 * Creates a new instance of the ColorSelector class. 2962 * @param {ColorSelectorInitOptions} options 2963 */ 2964 function ColorSelector(options) { 2965 if (!options.controlId) { 2966 throw "`controlId` is required."; 2967 } 2968 2969 this.allowMultiple = !!options.allowMultiple; 2970 this.controlId = options.controlId; 2971 2972 // Set JQuery selectors. 2973 this.$colorSelector = $("#" + this.controlId);
2974 this.$colorSelectorItemContainers = this.$colorSelector.children(".checkbox"); 2975 2976 // Set defaults for background color and perceived brightness. 2977 // These will be overwritten during initialization. 2978 this.backgroundColor = [255, 255, 255]; 2979 this.backgroundLuma = maxLuma; 2980 } 2981 2982 // #endregion 2983 2984 // #region Methods 2985 2986 /** 2987 * Initializes the color selector. 2988 */ 2989 ColorSelector.prototype.initialize = function () { 2990 var colorSelectorElement = this.$colorSelector[0]; 2991 2992 if (!colorSelectorElement) { 2993 throw "Unable to find color selector element with control ID " + this.controlId; 2994 } 2995 2996 // Set the background color of the color selector. 2997 this.backgroundColor = getInheritedBackgroundColor(colorSelectorElement); 2998 2999 // Set the perceived brightness of the color picker. 3000 // The perceived brightness is better for determining 3001 // when two colors are similar to the human eye 3002 // instead of comparing RGB values. 3003 this.backgroundLuma = ColorUtilities.rgbToLuma(this.backgroundColor[0], this.backgroundColor[1], this.backgroundColor[2]); 3004 3005 var $colorSelectorItemContainers = this.$colorSelectorItemContainers; 3006 3007 // Initialize the color items. 3008 var checkedFound = false; 3009 for (var i = 0; i < $colorSelectorItemContainers.length; i++) { 3010 var $colorSelectorItemContainer = $($colorSelectorItemContainers[i]); 3011 3012 // If only one selection is allowed, then only leave the first checked input as checked, 3013 // and uncheck the others. 3014 if (!this.allowMultiple) { 3015 var $input = findInput($colorSelectorItemContainer); 3016 3017 if ($input.prop("checked")) { 3018 if (checkedFound) { 3019 // A checked input was already found so mark this input as unchecked. 3020 $input.prop("checked", false); 3021 } 3022 3023 checkedFound = true; 3024 } 3025 } 3026 3027 initializeItem(this, $colorSelectorItemContainer); 3028 } 3029 }; 3030 3031 // #endregion 3032 3033 // #region Private Functions 3034 3035 /** 3036 * Finds the checkbox input for a color selector item container. 3037 * @param {JQuery<HTMLElement>} $colorSelectorItemContainer 3038 * @returns {JQuery<HTMLInputElement>} 3039 */ 3040 function findInput($colorSelectorItemContainer) { 3041 return $colorSelectorItemContainer.find('input[type="checkbox"]'); 3042 } 3043 3044 /** 3045 * Initializes the color items. 3046 * @param {ColorSelector} colorSelector 3047 * @param {JQuery<HTMLElement>} $colorSelectorItemContainer 3048 */ 3049 function initializeItem(colorSelector, $colorSelectorItemContainer) { 3050 $colorSelectorItemContainer.addClass("color-selector-item-container"); 3051 3052 var $checkboxLabel = $colorSelectorItemContainer.find("label"); 3053 $checkboxLabel.addClass("color-selector-item"); 3054 3055 var $checkboxInput = findInput($colorSelectorItemContainer); 3056 $checkboxInput.addClass("color-selector-item-checkbox"); 3057 3058 styleInput($checkboxInput); 3059 3060 // Update the checked state whenever it changes. 3061 $checkboxInput.on("click", function () { 3062 onCheckboxCheckChanged(colorSelector, $(this)); 3063 }); 3064 3065 // Remove the .checkbox CSS class as we don't want any checkbox styling. 3066 $colorSelectorItemContainer.removeClass("checkbox"); 3067 3068 // Set the label background color to the color found in the label text. 3069 var $labelText = $checkboxLabel.find(".label-text"); 3070 var itemHexColor = $labelText.text(); 3071 $checkboxLabel.css("background-color", itemHexColor); 3072 3073 // Remove the label text so just the color is displayed. 3074 $labelText.remove(); 3075 3076 // Determine if the item needs a border to separate it from the background. 3077 var itemRgb = ColorUtilities.hexToRgba(itemHexColor); 3078 var itemLuma = ColorUtilities.rgbToLuma(itemRgb[0], itemRgb[1], itemRgb[2]); 3079 3080 if (ColorUtilities.areColorsSimilar(colorSelector.backgroundLuma[0], colorSelector.backgroundLuma[1], colorSelector.backgroundLuma[2], itemLuma[0], itemLuma[1], itemLuma[2], colorSimilarityPercentage)) { 3081 $checkboxLabel.addClass(getCamouflagedClass(itemLuma[0], itemLuma[1], itemLuma[2])); 3082 } 3083 } 3084 3085 /** 3086 * Handles the color item checkbox changed event. 3087 * @param {ColorSelector} colorSelector The color selector. 3088 * @param {JQuery<HTMLInputElement>} $checkboxInput The checkbox input. 3089 */ 3090 function onCheckboxCheckChanged(colorSelector, $checkboxInput) { 3091 styleInput($checkboxInput); 3092 3093 if (!colorSelector.allowMultiple) { 3094 // For single select (radio) bevahior, clicking a checkbox should always check it. 3095 // If the checkbox is already checked, then return false to prevent the default behavior of unchecking it. 3096 3097 // Uncheck all other checkboxes.
3098 var $colorSelectorItemContainers = colorSelector.$colorSelectorItemContainers; 3099 3100 for (var i = 0; i < $colorSelectorItemContainers.length; i++) { 3101 var $otherItemInput = findInput($($colorSelectorItemContainers[i])); 3102 3103 if ($otherItemInput[0] !== $checkboxInput[0]) { 3104 $otherItemInput.prop("checked", false); 3105 styleInput($otherItemInput); 3106 } 3107 } 3108 } 3109 } 3110 3111 /** 3112 * 3113 * @param {JQuery<HTMLInputElement>} $input 3114 * @param {JQuery<HTMLElement | undefined>} $label 3115 * @param {JQuery<HTMLElement | undefined} $container 3116 */ 3117 function styleInput($input, $label, $container) { 3118 if (!$label) { 3119 $label = $input.parent("label"); 3120 } 3121 3122 if (!$container) { 3123 $container = $input.closest(".color-selector-item-container"); 3124 } 3125 3126 if ($input.prop("checked")) { 3127 $label.addClass("checked"); 3128 $container.addClass("checked"); 3129 } 3130 else { 3131 $label.removeClass("checked"); 3132 $container.removeClass("checked"); 3133 } 3134 } 3135 3136 /** 3137 * Gets the camouflaged CSS class for a luma color (rL, gL, bL). 3138 * @param {number} rLuma The R luma color. 3139 * @param {number} gLuma The G luma color. 3140 * @param {number} bLuma The B luma color. 3141 * @returns {string} The camouflaged CSS class for a luma color. 3142 */ 3143 function getCamouflagedClass(rLuma, gLuma, bLuma) { 3144 var luma = rLuma + gLuma + bLuma; 3145 if (luma > 128) { // Max is 255 3146 return camouflagedLightClass; 3147 } 3148 else { 3149 return camouflagedDarkClass; 3150 } 3151 } 3152 3153 /** 3154 * Gets the first RGBA background color that isn't the default, 3155 * starting from the element and working its way up the 3156 * parent element chain. 3157 * @param {Element} el 3158 * @returns {Number[]} The first RGBA background color that isn't the default; otherwise, the default color is returned. 3159 */ 3160 function getInheritedBackgroundColor(el) { 3161 3162 function getDefaultBackground() { 3163 var div = document.createElement("div"); 3164 document.head.appendChild(div); 3165 var bg = window.getComputedStyle(div).backgroundColor; 3166 document.head.removeChild(div); 3167 return bg; 3168 } 3169 3170 // Get default style for current browser. 3171 var defaultStyle = getDefaultBackground(); // typically "rgba(0, 0, 0, 0)" 3172 3173 // Start with the supplied element. 3174 var elementsToProcess = [ 3175 el 3176 ]; 3177 3178 while (elementsToProcess.length) { 3179 var elementToProcess = elementsToProcess.shift(); 3180 3181 if (!elementToProcess) { 3182 // Skip to the next element if the current one is nullish. 3183 continue; 3184 } 3185 3186 var backgroundColor = window.getComputedStyle(elementToProcess).backgroundColor; 3187 3188 // If we got a real value, return it. 3189 if (backgroundColor != defaultStyle) { 3190 return ColorUtilities.rgbFuncToRgba(backgroundColor); 3191 } 3192 // Otherwise, process the next parent element. 3193 else if (elementToProcess.parentElement) { 3194 elementsToProcess.push(elementToProcess.parentElement); 3195 } 3196 } 3197 3198 // If we have reached the top parent element without getting an explicit color, 3199 // return the default style. 3200 return ColorUtilities.rgbFuncToRgba(defaultStyle); 3201 } 3202 3203 // #endregion 3204 3205 // #endregion 3206 3207 // Keeps track of ColorSelector instances. 3208 var colorSelectors = {}; 3209 3210 var exports = { 3211 /** 3212 * Initializes a new ColorSelector instance. 3213 * @param {ColorSelectorInitOptions} options 3214 * @returns {ColorSelector} 3215 */ 3216 initialize: function (options) { 3217 var cs = new ColorSelector(options); 3218 cs.initialize(); 3219 3220 colorSelectors[options.controlId] = cs; 3221 3222 return cs; 3223 }, 3224 3225 /** 3226 * Gets a ColorSelector instance by control ID. 3227 * @param {string} controlId The control ID. 3228 * @returns {ColorSelector} 3229 */ 3230 get: function(controlId) { 3231 return colorSelectors[controlId]; 3232 }, 3233 3234 /** 3235 * Sets the camouflaged class if the element color is similar to the target element (or parent if not set). 3236 * @param {string} elementSelector The selector of the element to compare. 3237 * @param {string} targetSelector (Optional) The selector of the target element with which to compare colors. Defaults to the parent element. 3238 */ 3239 setCamouflagedClass: function (elementSelector, targetSelector) { 3240 var element = document.querySelector(elementSelector); 3241 var target = targetSelector ? document.querySelector(target
3241Selector) : element.parentElement; 3242 3243 var elementColor = getInheritedBackgroundColor(element); 3244 var targetColor = getInheritedBackgroundColor(target); 3245 3246 var elementLuma = ColorUtilities.rgbToLuma(elementColor[0], elementColor[1], elementColor[2]); 3247 var targetLuma = ColorUtilities.rgbToLuma(targetColor[0], targetColor[1], targetColor[2]); 3248 3249 var isSamePerceivedBrightness = ( 3250 elementLuma[0] === targetLuma[0] 3251 && elementLuma[1] === targetLuma[1] 3252 && elementLuma[2] === targetLuma[2] 3253 ); 3254 3255 // Short-circuit if elements have the same color. 3256 if (isSamePerceivedBrightness 3257 || ColorUtilities.areColorsSimilar(elementLuma[0], elementLuma[1], elementLuma[2], targetLuma[0], targetLuma[1], targetLuma[2], colorSimilarityPercentage)) { 3258 element.classList.add(getCamouflagedClass(elementLuma[0], elementLuma[1], elementLuma[2])); 3259 } 3260 else { 3261 element.classList.remove(camouflagedLightClass); 3262 element.classList.remove(camouflagedDarkClass); 3263 } 3264 } 3265 }; 3266 3267 return exports; 3268 }()); 3269}(jQuery)); 3270; 3271(function ($) 3272{ 3273 'use strict'; 3274 window.Rock = window.Rock || {}; 3275 Rock.controls = Rock.controls || {}; 3276 3277 Rock.controls.datePartsPicker = (function () 3278 { 3279 var exports = { 3280 initialize: function (options) 3281 { 3282 if (!options.id) { 3283 throw 'id is required'; 3284 } 3285 3286 }, 3287 clientValidate: function (validator, args) 3288 { 3289 var $datePartsPicker = $(validator).closest('.js-datepartspicker'); 3290 var monthNumber = Number($datePartsPicker.find('.js-month').val()); 3291 var dayNumber = Number($datePartsPicker.find('.js-day').val()); 3292 var yearNumber = Number($datePartsPicker.find('.js-year').val()); 3293 var required = $datePartsPicker.attr('data-required') == 'true'; 3294 var requireYear = $datePartsPicker.attr('data-requireyear') == 'true'; 3295 var allowFuture = $datePartsPicker.attr('data-allowFuture') == 'true'; 3296 var itemLabelText = $datePartsPicker.attr('data-itemlabel'); 3297 3298 var isValid = true; 3299 3300 if (!allowFuture) { 3301 if (monthNumber && dayNumber && yearNumber) { 3302 // NOTE: Javascript Date Contructor's month parameter is zero based! 3303 // see http://stackoverflow.com/questions/2552483/why-does-the-month-argument-range-from-0-to-11-in-javascripts-date-constructor 3304 var bDate = new Date(yearNumber, monthNumber-1, dayNumber); 3305 var now = new Date(); 3306 if (bDate > now) { 3307 isValid = false; 3308 validator.errormessage = itemLabelText + ' cannot be a future date.' 3309 } 3310 } 3311 } 3312 3313 if (monthNumber && dayNumber && (yearNumber || !requireYear)) { 3314 // month, day and (conditionally) year are all set, it's OK 3315 } 3316 else if (monthNumber || dayNumber || yearNumber) { 3317 // at least one of them is set, but some are not, so it is invalid 3318 isValid = false; 3319 validator.errormessage = itemLabelText + ' must be a valid value.'; 3320 } 3321 else if (required) { 3322 // nothing is set but it is a required field. 3323 isValid = false; 3324 validator.errormessage = itemLabelText + ' is required.'; 3325 } 3326 3327 var control = $datePartsPicker 3328 if (isValid) { 3329 control.removeClass('has-error'); 3330 } else { 3331 control.addClass('has-error'); 3332 } 3333 3334 args.IsValid = isValid; 3335 } 3336 }; 3337 3338 return exports; 3339 }()); 3340}(jQuery)); 3341; 3342(function ($) { 3343 'use strict'; 3344 window.Rock = window.Rock || {}; 3345 Rock.controls = Rock.controls || {}; 3346 3347 Rock.controls.datePicker = (function () { 3348 var exports = { 3349 initialize: function (options) { 3350 if (!options.id) { 3351 throw 'id is required'; 3352 } 3353 var dateFormat = 'mm/dd/yyyy'; 3354 if (options.format) { 3355 dateFormat = options.format; 3356 } 3357 3358 var $textBox = $('#' + options.id); 3359 3360 var $datePickerContainer = $textBox.closest('.js-date-picker-container'); 3361 var $datePickerInputGroup = $textBox.closest('.input-group.js-date-picker'); 3362 3363 // uses https://github.com/uxsolutions/bootstrap-datepicker 3364 var datePicker = $datePickerInputGroup.datepicker({ 3365 format: dateFormat, 3366 assumeNearbyYear: 10, 3367 autoclose: true, 3368 container: options.container || document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || "body", 3369 orientation: options.orientation || "auto", 3370 todayBtn: "linked", 3371 forceParse: options.forceParse, 3372 startDate: options.startDate, 3373 endDate: options.endDate || new Date(8640000000000000), 3374 startView: options.startView, 3375 showOnFocus: options.showOnFocus, 3376 todayHighlight: options.todayHighlight, 3377 zIndexOffset: 1050 3378 }); 3379
3380 // Listen to actions from external code. 3381 if (options.actions) { 3382 // Update the bootstrap datepicker when external code sets the date. 3383 options.actions.onSetLocalDate = (localDate) => { 3384 $datePickerInputGroup.data("datepicker") 3385 .setDate(localDate) // Set the date. 3386 .fill(); // Update the UI. 3387 }; 3388 } 3389 3390 // note: using 'change' instead of datePicker's 'changeDate' so that both manual entry and picking from calender works 3391 datePicker.on('change', function (e) { 3392 if (options.postbackScript) { 3393 window.location = "javascript:" + options.postbackScript; 3394 } 3395 3396 if (options.onChangeScript) { 3397 options.onChangeScript(); 3398 } 3399 }); 3400 3401 // if the guest clicks the addon select all the text in the input 3402 $datePickerInputGroup.find('.input-group-addon').on('click', function () { 3403 $(this).siblings('.form-control').select(); 3404 }); 3405 3406 $datePickerContainer.find('.js-current-date-checkbox').on('click', function (a, b, c) { 3407 var $dateOffsetBox = $datePickerContainer.find('.js-current-date-offset'); 3408 var $dateOffsetlabel = $("label[for='" + $dateOffsetBox.attr('id') + "']") 3409 if ($(this).is(':checked')) { 3410 $dateOffsetlabel.show(); 3411 $dateOffsetBox.show(); 3412 3413 // set textbox val to something instead of empty string so that validation doesn't complain 3414 $textBox.data("last-value", $textBox.val()).val('Current').prop('disabled', true).addClass('aspNetDisabled'); 3415 3416 } else { 3417 $dateOffsetlabel.hide(); 3418 $dateOffsetBox.hide(); 3419 3420 // set textbox val to last value so that validation will work again (if it is enabled) 3421 $textBox.val($textBox.data('last-value')).prop('disabled', false).removeClass('aspNetDisabled'); 3422 } 3423 }); 3424 } 3425 }; 3426 3427 return exports; 3428 }()); 3429}(jQuery)); 3430; 3431(function ($) 3432{ 3433 'use strict'; 3434 window.Rock = window.Rock || {}; 3435 Rock.controls = Rock.controls || {}; 3436 3437 Rock.controls.dateRangePicker = (function () 3438 { 3439 var exports = { 3440 initialize: function (options) 3441 { 3442 if (!options.id) { 3443 throw 'id is required'; 3444 } 3445 }, 3446 clientValidate: function (validator, args) 3447 { 3448 var $dateRangePicker = $(validator).closest('.js-daterangepicker'); 3449 var lowerValue = $dateRangePicker.find('.js-lower input').val(); 3450 var upperValue = $dateRangePicker.find('.js-upper input').val(); 3451 var required = $dateRangePicker.attr('data-required') == 'true'; 3452 var itemLabelText = $dateRangePicker.attr('data-itemlabel'); 3453 3454 var isValid = true; 3455 3456 if (required) { 3457 // if required, then make sure that the date range has a start and/or end date (can't both be blank) 3458 if (lowerValue.length == 0 && upperValue.length == 0) { 3459 isValid = false; 3460 validator.errormessage = itemLabelText + " is required"; 3461 } 3462 } 3463 3464 var control = $dateRangePicker; 3465 if (isValid) 3466 { 3467 control.removeClass('has-error'); 3468 } 3469 else 3470 { 3471 control.addClass('has-error'); 3472 } 3473 3474 args.IsValid = isValid; 3475 } 3476 }; 3477 3478 return exports; 3479 }()); 3480}(jQuery)); 3481; 3482(function ($) { 3483 'use strict'; 3484 window.Rock = window.Rock || {}; 3485 Rock.controls = Rock.controls || {}; 3486 3487 Rock.controls.dateTimePicker = (function () { 3488 var exports = { 3489 initialize: function (options) { 3490 if (!options.id) { 3491 throw 'id is required'; 3492 } 3493 var dateFormat = 'mm/dd/yyyy'; 3494 if (options.format) { 3495 dateFormat = options.format; 3496 } 3497 3498 var $dp = $('#' + options.id + " .js-datetime-date"); 3499 3500 var $dateTimePickerContainer = $dp.closest('.js-datetime-picker-container'); 3501 var $dateTimePickerInputGroup = $dp.closest('.input-group.date'); 3502 3503 // uses https://github.com/uxsolutions/bootstrap-datepicker 3504 $dateTimePickerInputGroup.datepicker({ 3505 format: dateFormat, 3506 assumeNearbyYear: 10, 3507 autoclose: true, 3508 todayBtn: "linked", 3509 startView: options.startView || 'month', 3510 todayHighlight: options.todayHighlight || true, 3511 zIndexOffset: 1050 3512 }); 3513 3514 // if the guest clicks the addon select all the text in the input 3515 $dateTimePickerInputGroup.find('.input-group-addon').on('click', function () 3516 { 3517 $(this).siblings('.form-control').select(); 3518 }); 3519 3520 var $tp = $('#' + options.id + " .js-datetime-time"); 3521 if ($tp) { 3522 var $tpid = $tp.attr('id'); 3523 if ($tpid) { 3524 Rock.controls.timePicker.initialize({ 3525 id: $tpid 3526 }); 3527 } 3528 } 3529 3530 $dateTimePickerContainer.find('.js-current-datetime-checkbox').on('click', function (a, b, c) {
3531 var $dateTimeOffsetBox = $dateTimePickerContainer.find('.js-current-datetime-offset'); 3532 var $dateOffsetlabel = $("label[for='" + $dateTimeOffsetBox.attr('id') + "']") 3533 if ($(this).is(':checked')) { 3534 $dateOffsetlabel.removeClass('aspNetDisabled').show(); 3535 $dateTimeOffsetBox.show(); 3536 $dateTimeOffsetBox.prop('disabled', false).removeClass('aspNetDisabled').val( $dateTimeOffsetBox.data('last-value') ); 3537 $dp.data( "last-value", $dp.val()).val('').prop('disabled', true).addClass('aspNetDisabled').prop('placeholder', 'Current'); 3538 $tp.data( "last-value", $tp.val()).val('').prop('disabled', true).addClass('aspNetDisabled'); 3539 } else { 3540 $dateOffsetlabel.addClass('aspNetDisabled').hide(); 3541 $dateTimeOffsetBox.data( "last-value", $dateTimeOffsetBox.val()).hide(); 3542 $dateTimeOffsetBox.val('').prop('disabled', true).addClass('aspNetDisabled'); 3543 $dp.prop('disabled', false).removeClass('aspNetDisabled').prop('placeholder', '').val($dp.data('last-value')); 3544 $tp.prop('disabled', false).removeClass('aspNetDisabled').val($tp.data('last-value')); 3545 } 3546 }); 3547 } 3548 }; 3549 3550 return exports; 3551 }()); 3552}(jQuery)); 3553; 3554(function ($) { 3555 'use strict'; 3556 window.Rock = window.Rock || {}; 3557 Rock.controls = Rock.controls || {}; 3558 3559 Rock.controls.electronicSignatureControl = (function () { 3560 var exports = { 3561 /** 3562 * 3563 * @param {any} options 3564 */ 3565 initialize: function (options) { 3566 if (!options.controlId) { 3567 throw 'id is required'; 3568 } 3569 3570 var self = this; 3571 3572 self.ImageMimeType = options.imageMimeType || "image/png"; 3573 3574 var $control = $('#' + options.controlId); 3575 3576 if ($control.length == 0) { 3577 return; 3578 } 3579 3580 self.$signatureControl = $control; 3581 self.$signatureTyped = $('.js-signature-typed', $control); 3582 3583 self.$signatureEntryDrawn = $('.js-signature-entry-drawn', $control); 3584 self.$signaturePadCanvas = $('.js-signature-pad-canvas', $control); 3585 self.$signatureImageDataURL = $('.js-signature-data', $control); 3586 3587 self.$clearSignature = $('.js-clear-signature', $control); 3588 self.$saveSignature = $('.js-save-signature', $control); 3589 3590 self.initializeSignaturePad(); 3591 3592 self.$saveSignature.click(function () { 3593 if (!self.signatureIsValid(self.$signatureControl)) { 3594 return false; 3595 } 3596 3597 if (self.$signaturePadCanvas.length) { 3598 var signaturePad = self.$signaturePadCanvas.data('signatureComponent'); 3599 3600 if (signaturePad) { 3601 var signatureImageDataUrl = signaturePad.toDataURL(self.ImageMimeType); 3602 self.$signatureImageDataURL.val(signatureImageDataUrl); 3603 } 3604 } 3605 }); 3606 }, 3607 3608 /** */ 3609 initializeSignaturePad: function () { 3610 var self = this; 3611 if (!self.$signaturePadCanvas.length) { 3612 return 3613 } 3614 3615 var signaturePadOptions = {}; 3616 if (self.ImageMimeType == "image/jpeg") { 3617 // NOTE That if we use jpeg, we'll have to deal with this https://github.com/szimek/signature_pad/issues/584 3618 signaturePadOptions = { 3619 penColor: 'black', 3620 backgroundColor: 'white' 3621 } 3622 }; 3623 3624 var signaturePad = new SignaturePad(self.$signaturePadCanvas[ 0 ], signaturePadOptions); 3625 3626 self.$signaturePadCanvas.data('signatureComponent', signaturePad); 3627 3628 self.$clearSignature.click(function () { 3629 signaturePad.clear(); 3630 }) 3631 3632 window.addEventListener("resize", function () { 3633 self.resizeSignatureCanvas(self) 3634 }); 3635 3636 self.resizeSignatureCanvas(self); 3637 }, 3638 3639 /** */ 3640 resizeSignatureCanvas: function (signatureControl) { 3641 if (!signatureControl.$signaturePadCanvas.length) { 3642 return; 3643 } 3644 3645 // If the window is resized, that'll affect the drawing canvas 3646 // also, if there is an existing signature, it'll get messed up, so clear it and 3647 // make them sign it again. See additional details why 3648 // https://github.com/szimek/signature_pad 3649 var signaturePadCanvas = signatureControl.$signaturePadCanvas[ 0 ]; 3650 var containerWidth = signatureControl.$signatureEntryDrawn.width(); 3651 if (!containerWidth || containerWidth == 0) { 3652 containerWidth = 400; 3653 } 3654 3655 // Note the suggestion https://github.com/szimek/signature_pad#handling-high-dpi-screens 3656 // to re-calculate the ratio based on window.devicePixelRatio isn't needed. 3657 // We can just use the width() of the container and use fixed height of 100. 3658 const ratio = 1; 3659 signaturePadCanvas.width = containerWidth * ratio; 3660 signaturePadCanvas.height = 100 * ratio; 3661 signaturePadCanvas.getContext("2d").scale(ratio, ratio); 3662 3663 var signaturePad = signatureControl.$signaturePadCanvas.data('signatureComponent'); 3664 signaturePad.clear(); 3665 }, 3666 3667 signatureIsValid: function ($signatureControl) { 3668 var $signatureCanvas = $signatureControl.find('.js-signature-pad-canvas') 3669 var $signatureTyped = $signatureControl.find('.js-signature-typed'); 3670 3671 if ($signatureCanvas.length == 0 && $signatureTyped.length == 0) { 3672 return; 3673 } 3674 3675 var isValid = true; 3676 3677 if ($signatureCanvas.length) { 3678 var signatureCanvas = $signatureCanvas.data('signatureComponent'); 3679 3680 if (signatureCanvas.isEmpty()) { 3681 isValid = false; 3682 } 3683 } 3684 else { 3685 if ($signatureTyped.val().trim() == '') { 3686 isValid = false; 3687 } 3688 } 3689 3690 return isValid; 3691 }, 3692 3693 /** */ 3694 clientValidate: function (validator, args) { 3695 var $signatureControl = $(validator).closest('.js-electronic-signature-control'); 3696 3697 var isValid = this.signatureIsValid($signatureControl); 3698 3699 if (!isValid) { 3700 validator.errormessage = "Signature is required."; 3701 } 3702 3703 args.IsValid = isValid; 3704 } 3705 }; 3706 3707 return exports; 3708 }()); 3709}(jQuery)); 3710; 3711(function (Sys) { 3712 'use strict'; 3713 Sys.Application.add_load(function () { 3714 3715 var preventDoublePostback = function () { 3716 var inputOnChange = $(this).attr('onchange'); 3717 var inputOnClick = $(this).attr('onclick'); 3718 if (inputOnChange && inputOnChange.indexOf('__doPostBack') > 0 || inputOnClick && inputOnClick.indexOf('__doPostBack') > 0) { 3719 var $blockButtons = $(this).closest('.js-block-instance').find('a'); 3720 $blockButtons.attr('disabled', 'disabled') 3721 .prop('disabled', true) 3722 .attr('disabled', 'disabled') 3723 .addClass('disabled'); 3724 } 3725 }; 3726 3727 // since some of the edit controls do a postback to show/hide other controls, we want to disable buttons to prevent a double postback 3728 $('select.js-prevent-double-postback,input.js-prevent-double-postback').on('change', preventDoublePostback); 3729 $('.js-prevent-double-postback input[type="radio"]').on('click', preventDoublePostback); 3730 });
3731}(Sys)); 3732; 3733(function ($) { 3734 'use strict'; 3735 window.Rock = window.Rock || {}; 3736 Rock.controls = Rock.controls || {}; 3737 3738 Rock.controls.fileUploader = (function () { 3739 var _configure = function (options) { 3740 options.isBinaryFile = options.isBinaryFile || 'T'; 3741 options.uploadUrl = options.uploadUrl || 'FileUploader.ashx'; 3742 3743 var wsUrl = Rock.settings.get('baseUrl') 3744 + options.uploadUrl + '?' 3745 + 'isBinaryFile=' + options.isBinaryFile; 3746 3747 if (options.isBinaryFile === 'T') { 3748 wsUrl += '&fileId=' + options.fileId 3749 + '&fileTypeGuid=' + options.fileTypeGuid; 3750 } 3751 else { 3752 // note rootFolder is encrypted to prevent direct access to filesystem via the URL 3753 wsUrl += '&rootFolder=' + (encodeURIComponent(options.rootFolder) || ''); 3754 } 3755 3756 if (options.isTemporary === 'F') { 3757 wsUrl += '&IsTemporary=False'; 3758 } 3759 3760 if (options.parentEntityTypeId !== 'null') { 3761 wsUrl += '&ParentEntityTypeId=' + options.parentEntityTypeId; 3762 } 3763 3764 if (options.parentEntityId !== 'null') { 3765 wsUrl += '&ParentEntityId=' + options.parentEntityId; 3766 } 3767 3768 // uses https://github.com/blueimp/jQuery-File-Upload 3769 $('#' + options.controlId).fileupload({ 3770 url: wsUrl, 3771 dataType: 'json', 3772 dropZone: $('#' + options.controlId).closest('.fileupload-dropzone'), 3773 autoUpload: true, 3774 submit: options.submitFunction, 3775 sequentialUploads: true, 3776 start: function (e, data) { 3777 var $el = $('#' + options.controlId).closest('.fileupload-group'); 3778 $el.find('.js-upload-progress').rockFadeIn(); 3779 }, 3780 progressall: function (e, data) { 3781 try { 3782 if (data.total > 0) { 3783 var $el = $('#' + options.controlId).closest('.fileupload-group'); 3784 var $progressPercent = $el.find('.js-upload-progress-percent'); 3785 if (!$progressPercent.length) { 3786 return; 3787 } 3788 3789 var percent = (data.loaded * 100 / data.total).toFixed(0); 3790 if (percent > 1 && percent < 99) { 3791 $progressPercent.text(percent + "%"); 3792 } 3793 else { 3794 $progressPercent.text(""); 3795 } 3796 } 3797 } 3798 catch (ex) { 3799 // ignore if any exception occurs 3800 } 3801 }, 3802 stop: function (e) { 3803 var $el = $('#' + options.controlId).closest('.fileupload-group'); 3804 $el.find('.js-upload-progress').hide(); 3805 $el.find('.fileupload-dropzone').show(); 3806 }, 3807 done: function (e, data) { 3808 var $el = $('#' + options.aFileName); 3809 3810 if ((options.isBinaryFile || 'T') === 'F') { 3811 $('#' + options.hfFileId).val(data.response().result.FileName); 3812 } 3813 else { 3814 $('#' + options.hfFileId).val(data.response().result.Id); 3815 } 3816 3817 var getFileUrl = Rock.settings.get('baseUrl') 3818 + 'GetFile.ashx?' 3819 + 'isBinaryFile=' + (options.isBinaryFile || 'T') 3820 // note rootFolder is encrypted to prevent direct access to filesystem via the URL 3821 + '&rootFolder=' + (encodeURIComponent(options.rootFolder) || ''); 3822 3823 if (options.disablePredictableIds) { 3824 getFileUrl += '&guid=' + data.response().result.Guid; 3825 } 3826 else { 3827 getFileUrl += '&id=' + data.response().result.Id 3828 } 3829 3830 getFileUrl += '&fileName=' + data.response().result.FileName; 3831 3832 $el.text(data.response().result.FileName).attr('href', getFileUrl); 3833 $('#' + options.aRemove).show(); 3834 if (options.postbackScript) { 3835 window.location = "javascript:" + options.postbackScript; 3836 } 3837 3838 if (options.doneFunction) { 3839 options.doneFunction(e, data); 3840 } 3841 }, 3842 fail: function (e, data) { 3843 var $el = $('#' + options.controlId).closest('.fileupload-group'); 3844 $el.siblings('.js-rockupload-alert').remove(); 3845 var $warning = $('<div class="alert alert-warning alert-dismissable js-rockupload-alert"/>'); 3846 3847 var msg = "unable to upload"; 3848 if (data.response().jqXHR && data.response().jqXHR.status == 406) { 3849 msg = "file type not allowed"; 3850 } else if (data.response().jqXHR && data.response().jqXHR.responseText) { 3851 msg = data.response().jqXHR.responseText; 3852 } 3853 3854 if (options.maxUploadBytes && data.total) { 3855 if (data.total >= options.maxUploadBytes) { 3856 msg = "file size is limited to " + (options.maxUploadBytes / 1024 / 1024) + "MB"; 3857 } 3858 } 3859 3860 $warning.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>') 3861 .append('<strong><i class="ti ti-alert-triangle"></i> Warning </strong>') 3862 .append(msg); 3863 $warning.insertBefore($el); 3864 } 3865 }); 3866 3867 $('#' + options.aRemove).on('click', function () { 3868 $(this).hide(); 3869 var $el = $('#' + options.aFileName); 3870 $el.attr('href', '#'); 3871 $el.text(''); 3872 $el.removeClass('file-exists'); 3873 3874 if (options.postbackRemovedScript) { 3875 window.location = "javascript:" + options.postbackRemovedScript; 3876 } else { 3877 $('#' + options.hfFileId).val('0'); 3878 } 3879 3880 return false; 3881 }); 3882 }, 3883 exports = { 3884 initialize: function (options) { 3885 if (!options.controlId) throw 'Control ID must be set.'; 3886 _configure(options); 3887 } 3888 }; 3889 3890 return exports; 3891 }()); 3892}(jQuery)); 3893; 3894(function ($) { 3895 'use strict'; 3896 window.Rock = window.Rock || {}; 3897 Rock.controls = Rock.controls || {}; 3898 3899 Rock.controls.firstNameTextBox = (function () { 3900 const exports = { 3901 initialize: function (options) { 3902 if (!options.id) { 3903 throw new Error('id is required'); 3904 } 3905 this.options = options; 3906 }, 3907 clientValidate: function (validator, args) { 3908 const firstNameTextBox = document.getElementById(this.options.id); 3909 const notAllowedStrings = this.options.notAllowedStrings; 3910 let firstName = undefined; 3911 if (firstNameTextBox) { 3912 firstName = firstNameTextBox.value.toUpperCase(); 3913 } 3914 3915 let isValid = true; 3916 let invalidString; 3917 3918 if (firstName) { 3919 for (const value of notAllowedStrings) { 3920 if (firstName.includes(value.toUpperCase())) { 3921 isValid = false; 3922 invalidString = value; 3923 break; 3924 } 3925 } 3926 } 3927 if (!isValid) { 3928 const labelText = firstNameTextBox.dataset.itemLabel; 3929 validator.errormessage = `${labelText} cannot contain: ${invalidString}`; 3930 3931 if (this.options.displayInlineValidationError) { 3932 validator.innerHTML = validator.errormessage; 3933 } 3934 } 3935 3936 args.IsValid = isValid; 3937 } 3938 }; 3939 3940 return exports; 3941 }()); 3942}(jQuery)); 3943; 3944(function ($) { 3945 'use strict'; 3946 window.Rock = window.Rock || {}; 3947 Rock.controls = Rock.controls || {}; 3948 3949 Rock.controls.followingsToggler = (function () { 3950 3951 var exports = { 3952 // initialize a script that will set the following status via REST 3953 // entityTypeId is the EntityType, and entityId is the .Id for the associated entity 3954 // personId and personAliasId are the person that is doing the following/un-following 3955 // callback is optional and called on follow or unfollow events with a param indicating 3956 // if the person is now following 3957 initialize: function ($followingDiv, entityTypeId, entityId, purposeKey, personId, personAliasId, callback) { 3958 var hasCallback = typeof callback === 'function'; 3959 3960 $followingDiv.on('click', function () { 3961 if ($followingDiv.hasClass('following')) { 3962 3963 $.ajax({
3964 type: 'DELETE', 3965 url: Rock.settings.get('baseUrl') + 'api/followings/' + entityTypeId + '/' + entityId + '/' + personId + '?purposeKey=' + encodeURIComponent(purposeKey), 3966 success: function (data, status, xhr) { 3967 $followingDiv.removeClass('following'); 3968 3969 // update the tooltip (if one was configured) 3970 if ($followingDiv.attr('data-original-title')) { 3971 $followingDiv.attr('data-original-title', 'Click to follow'); 3972 } 3973 3974 if (hasCallback) { 3975 callback(false, $followingDiv, entityTypeId, entityId, personId, personAliasId); 3976 } 3977 }, 3978 }); 3979 3980 } else { 3981 var following = { 3982 EntityTypeId: entityTypeId, 3983 EntityId: entityId, 3984 PersonAliasId: personAliasId, 3985 PurposeKey: purposeKey 3986 }; 3987 3988 $.ajax({
3989 type: 'POST', 3990 contentType: 'application/json', 3991 data: JSON.stringify(following), 3992 url: Rock.settings.get('baseUrl') + 'api/followings', 3993 statusCode: { 3994 201: function () { 3995 $followingDiv.addClass('following'); 3996 3997 // update the tooltip (if one was configured) 3998 if ($followingDiv.attr('data-original-title')) { 3999 $followingDiv.attr('data-original-title', 'Currently following'); 4000 } 4001 4002 if (hasCallback) { 4003 callback(true, $followingDiv, entityTypeId, entityId, personId, personAliasId); 4004 } 4005 } 4006 } 4007 }); 4008 } 4009 }) 4010 } 4011 }; 4012 4013 return exports; 4014 }()); 4015}(jQuery)); 4016; 4017(function ($) { 4018 'use strict'; 4019 window.Rock = window.Rock || {}; 4020 Rock.controls = Rock.controls || {}; 4021 4022 Rock.controls.fullScreen = (function () { 4023 var exports = { 4024 initialize: function (el) { 4025 $('.js-fullscreen-trigger').on('click', function (e) { 4026 e.preventDefault(); 4027 var elem = $(this).closest('.block-instance')[0] || document.documentElement 4028 if (el) { 4029 // If Requesting Fullscreen page, verify layout is fullscreen capable. 4030 if (el === 'body' && $(this).parents('.page-fullscreen-capable').length < 1) { 4031 elem = $(this).closest('.block-instance')[0] 4032 } else { 4033 elem = $(el)[0]; 4034 } 4035 } 4036 Rock.controls.fullScreen.toggleFullscreen(elem); 4037 }); 4038 }, 4039 toggleFullscreen: function (elem) { 4040 elem = elem || document.documentElement; 4041 if (!document.fullscreenElement && !document.mozFullScreenElement && 4042 !document.webkitFullscreenElement && !document.msFullscreenElement) { 4043 if (elem.requestFullscreen) { 4044 elem.requestFullscreen(); 4045 document.addEventListener('fullscreenchange', this.exitHandler, false); 4046 } else if (elem.msRequestFullscreen) { 4047 elem.msRequestFullscreen(); 4048 document.addEventListener('MSFullscreenChange', this.exitHandler, false); 4049 } else if (elem.mozRequestFullScreen) { 4050 elem.mozRequestFullScreen(); 4051 document.addEventListener('mozfullscreenchange', this.exitHandler, false); 4052 } else if (elem.webkitRequestFullscreen) { 4053 elem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); 4054 document.addEventListener('webkitfullscreenchange', this.exitHandler, false); 4055 } 4056 $(elem).addClass('is-fullscreen'); 4057 } else { 4058 if (document.exitFullscreen) { 4059 document.exitFullscreen(); 4060 } else if (document.msExitFullscreen) { 4061 document.msExitFullscreen(); 4062 } else if (document.mozCancelFullScreen) { 4063 document.mozCancelFullScreen(); 4064 } else if (document.webkitExitFullscreen) { 4065 document.webkitExitFullscreen(); 4066 } 4067 $(elem).removeClass('is-fullscreen'); 4068 } 4069 }, 4070 exitHandler: function (elem) { 4071 if (!document.webkitIsFullScreen && !document.mozFullScreen && !document.msFullscreenElement) { 4072 $(elem.target).removeClass('is-fullscreen'); 4073 document.dispatchEvent(new Event('RockExitFullscreen')); 4074 } 4075 } 4076 }; 4077 4078 return exports; 4079 }()); 4080}(jQuery)); 4081; 4082(function ($) { 4083 'use strict'; 4084 window.Rock = window.Rock || {}; 4085 Rock.controls = Rock.controls || {}; 4086 4087 Rock.controls.geoPicker = (function () { 4088 4089 var GeoPicker = function (options) { 4090 var obj = this; 4091 obj.controlId = options.controlId; 4092 obj.restUrl = options.restUrl; 4093 obj.path = options.path; 4094 obj.centerAddress = options.centerAddress; // used when nothing is on map 4095 obj.centerLatitude = options.centerLatitude || "33.590795"; // used when nothing is on map 4096 obj.centerLongitude = options.centerLongitude || "-112.126459"; // used when nothing is on map 4097 obj.drawingMode = options.drawingMode || "Polygon" || "Point"; // the available modes 4098 obj.strokeColor = options.strokeColor || "#0088cc"; 4099 obj.fillColor = options.fillColor || "#0088cc"; 4100 obj.mapId = options.mapId; 4101 4102 // An array of styles that controls the look of the Google Map 4103 // http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html 4104 obj.styles = options.mapStyle; 4105 4106 // This is used to temporarily store in case of user cancel. 4107 obj.pathTemp = null; 4108 4109 // the selected polygon or point 4110 obj.selectedShape = null; 4111 4112 // these are used to set the map's viewport boundary 4113 obj.minLat = null; 4114 obj.maxLat = null; 4115 obj.minLng = null; 4116 obj.maxLng = null; 4117 4118 // our google Map 4119 obj.map = null; 4120 4121 // an instance of the Google Maps drawing manager 4122 obj.drawingManager = null; 4123 4124 // An array of styles that controls the look of the Google Map 4125 // http://gmaps-samples-v3.googlecode.com/svn/trunk/styledmaps/wizard/index.html 4126 //obj.styles = [ 4127 // { 4128 // "stylers": [ 4129 // { "visibility": "simplified" }, 4130 // { "saturation": -100 } 4131 // ] 4132 // }, { 4133 // "featureType": "road", 4134 // "elementType": "labels", 4135 // "stylers": [ 4136 // { "visibility": "on" } 4137 // ] 4138 // }, { 4139 // "featureType": "poi", 4140 // "stylers": [ 4141 // { "visibility": "off" } 4142 // ] 4143 // }, { 4144 // "featureType": "landscape", 4145 // "stylers": [ 4146 // { "visibility": "off" } 4147 // ] 4148 // }, { 4149 // "featureType": "administrative.province", 4150 // "stylers": [ 4151 // { "visibility": "on" } 4152 // ] 4153 // }, { 4154 // "featureType": "administrative.locality", 4155 // "stylers": [ 4156 // { "visibility": "on" } 4157 // ] 4158 // } 4159 //]; 4160 4161 /** 4162 * Initializes the map viewport boundary coordinates. 4163 */ 4164 this.initMinMaxLatLng = function () { 4165 obj.minLat = null; 4166 obj.maxLat = null; 4167 obj.minLng = null; 4168 obj.maxLng = null; 4169 } 4170 4171 /** 4172 * Unselects the selected shape (if selected) and disables the delete button. 4173 */ 4174 this.clearSelection = function () { 4175 if (obj.selectedShape) { 4176 if (obj.drawingMode == "Polygon") { 4177 obj.selectedShape.setEditable(false); 4178 } 4179 obj.selectedShape = null; 4180 } 4181 $('#gmnoprint-delete-button_' + obj.controlId).attr('disabled', '') 4182 .find('.ti-x').css("color", "#aaa"); 4183 } 4184 4185 /** 4186 * Stores the given shape's path points into the path property as a 4187 * pipe (|) delimited list of lat,long coordinates. This method also 4188 * re-enables the delete button so that the polygon can be deleted. 4189 */ 4190 this.setSelection = function (shape, type) { 4191 obj.clearSelection(); 4192 4193 // enable delete button 4194 $('#gmnoprint-delete-button_' + obj.controlId).prop("disabled", false) 4195 .find('.ti-x').css("color", ""); 4196 4197 obj.selectedShape = shape; 4198 4199 if (type == "polygon") { 4200 shape.setEditable(true); 4201 var coordinates = new Array(); 4202 var vertices = shape.getPaths().getAt(0); 4203 // Iterate over the vertices of the shape's path 4204 for (var i = 0; i < vertices.length; i++) { 4205 var xy = vertices.getAt(i); 4206 coordinates[i] = xy.toUrlValue(); 4207 } 4208 4209 // if the last vertex is not already the first, then 4210 // add the first vertex to the end of the path. 4211 if (vertices.getAt(0).toUrlValue() != coordinates[coordinates.length - 1]) { 4212 obj.path = coordinates.join('|') + '|' + vertices.getAt(0).toUrlValue(); 4213 } 4214 else { 4215 obj.path = coordinates.join('|'); 4216 } 4217 } 4218 else if (type == "marker") { 4219 obj.path = shape.getPosition().toUrlValue(); 4220 } 4221 } 4222 4223 /** 4224 * Delete the selected shape and enable the drawing controls 4225 * if they were deleted. Also removes the polygon from the hidden variable. 4226 */ 4227 this.deleteSelectedShape = function () { 4228 if (obj.selectedShape && confirm("Delete selected shape?")) { 4229 obj.selectedShape.setMap(null); 4230 obj.clearSelection(); 4231 4232 // delete the path 4233 obj.path = null; 4234 4235 // enable the drawing controls again 4236 obj.drawingManager.setOptions({ 4237 drawingControlOptions: { 4238 drawingModes: obj.getDrawingModes() 4239 } 4240 }); 4241 } 4242 } 4243 4244 /** 4245 * Returns the appropriate mode array for use with the map's DrawingManager 4246 * drawing control options. 4247 */ 4248 this.getDrawingModes = function getDrawingModes() { 4249 4250 if (obj.drawingMode == "Polygon") { 4251 return [google.maps.drawing.OverlayType.POLYGON]; 4252 } 4253 else if (obj.drawingMode == "Point") { 4254 return [google.maps.drawing.OverlayType.MARKER]; 4255 } 4256 } 4257 4258 /** 4259 * Returns a marker image styled according to the stroke color. 4260 */ 4261 this.getMarkerImage = function getMarkerImage() { 4262 return { 4263 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', 4264 fillColor: '#FE7569', 4265 fillOpacity: 1, 4266 strokeColor: '#000', 4267 strokeWeight: 1, 4268 scale: 1, 4269 labelOrigin: new google.maps.Point(0, -28) 4270 } 4271 } 4272 4273 /**
4274 * Finds the point/polygon boundary and sets the map viewport to fit 4275 */ 4276 this.fitBounds = function () { 4277 if (! obj.path) { 4278 // if no path, then set the center using the options 4279 var newLatLng = new google.maps.LatLng( 4280 parseFloat(obj.centerLatitude), 4281 parseFloat(obj.centerLongitude)); 4282 obj.map.setCenter(newLatLng); 4283 return; 4284 } 4285 4286 var coords = obj.path.split('|'); 4287 var pathArray = new Array(); 4288 // find the most southWest and northEast points of the path. 4289 for (var i = 0; i < coords.length ; i++) { 4290 var latLng = coords[i].split(','); 4291 var lat = parseFloat(latLng[0]); 4292 var lng = parseFloat(latLng[1]); 4293 // find the most southWest and northEast points of the path. 4294 obj.findBounds(lat, lng); 4295 pathArray.push(new google.maps.LatLng(lat, lng)); 4296 } 4297 4298 // Set the viewport to contain the given bounds. 4299 var southWest = new google.maps.LatLng(obj.minLat, obj.minLng); 4300 var northEast = new google.maps.LatLng(obj.maxLat, obj.maxLng); 4301 var bounds = new google.maps.LatLngBounds(southWest, northEast); 4302 obj.map.fitBounds(bounds); 4303 4304 if (obj.drawingMode == "Point") { 4305 obj.map.setZoom(16); 4306 } 4307 } 4308 4309 /** 4310 * Utility method to determine the most SouthWestern 4311 * and NorthEastern lat and long. 4312 */ 4313 this.findBounds = function (lat, lng) { 4314 if (!obj.minLat || lat < obj.minLat) { 4315 obj.minLat = lat; 4316 } 4317 if (!obj.maxLat || lat > obj.maxLat) { 4318 obj.maxLat = lat; 4319 } 4320 if (!obj.minLng || lng < obj.minLng) { 4321 obj.minLng = lng; 4322 } 4323 if (!obj.maxLng || lng > obj.maxLng) { 4324 obj.maxLng = lng; 4325 } 4326 } 4327 4328 /** 4329 * Disables the drawing manager so they cannot add anything to the map. 4330 */ 4331 this.disableDrawingManager = function () { 4332 // Switch back to non-drawing mode after drawing a shape. 4333 if (!obj.drawingManager) { 4334 return; 4335 } 4336 4337 obj.drawingManager.setDrawingMode(null); 4338 4339 // disable the drawing controls so we only get one polygon 4340 // and we'll add it back on deleting the existing polygon. 4341 obj.drawingManager.setOptions({ 4342 drawingControlOptions: { 4343 drawingModes: [ 4344 ] 4345 } 4346 }); 4347 } 4348 4349 /** 4350 * Takes the path data stored in the path and plots it on the map. 4351 */ 4352 this.plotPath = function (map) { 4353 obj.initMinMaxLatLng(); 4354 // only try this if we have a path 4355 if (obj.path) { 4356 var coords = obj.path.split('|'); 4357 var pathArray = new Array(); 4358 // put the polygon coordinates into a path array 4359 for (var i = 0; i < coords.length ; i++) { 4360 var latLng = coords[i].split(','); 4361 var lat = parseFloat(latLng[0]); 4362 var lng = parseFloat(latLng[1]); 4363 pathArray.push(new google.maps.LatLng(lat, lng)); 4364 } 4365 4366 // reverse geocode the first point to an address and display. 4367 var $label = $('#selectedGeographyLabel_' + this.controlId); 4368 obj.toAddress(pathArray[0], $label); 4369 4370 if (coords.length > 0) { 4371 var polygon; 4372 4373 if (obj.drawingMode == "Polygon") { 4374 4375 var polygon = new google.maps.Polygon({ 4376 path: pathArray, 4377 clickable: true, 4378 editable: true, 4379 strokeColor: obj.strokeColor, 4380 fillColor: obj.fillColor, 4381 strokeWeight: 2 4382 });
4383 polygon.setMap(map); 4384 4385 // Select the polygon 4386 obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON); 4387 4388 // Disable the drawing manager 4389 obj.disableDrawingManager(); 4390 4391 // add listener for moving polygon points. 4392 google.maps.event.addListener(polygon.getPath(), 'set_at', function (e) { 4393 obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON); 4394 }); 4395 4396 // add listener for adding new points. 4397 google.maps.event.addListener(polygon.getPath(), 'insert_at', function (e) { 4398 obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON); 4399 }); 4400 4401 // Add an event listener to implement right-click to delete node 4402 google.maps.event.addListener(polygon, 'rightclick', function (ev) { 4403 if (ev.vertex != null) { 4404 polygon.getPath().removeAt(ev.vertex); 4405 } 4406 obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON); 4407 }); 4408 4409 // add listener for "selecting" the polygon 4410 // because that's where we stuff the coordinates into the hidden variable 4411 google.maps.event.addListener(polygon, 'click', function () { 4412 obj.setSelection(polygon, google.maps.drawing.OverlayType.POLYGON); 4413 }); 4414 } 4415 else if (obj.drawingMode == "Point") { 4416 var point; 4417 4418 if (!obj.mapId) { 4419 point = new google.maps.Marker({ 4420 position: pathArray[0], 4421 map: map, 4422 clickable: true, 4423 icon: obj.getMarkerImage() 4424 }); 4425 } 4426 else { 4427 var pin = new google.maps.marker.PinElement({ 4428 background: '#FE7569', 4429 borderColor: '#000', 4430 scale: 1, 4431 glyph: '' 4432 }); 4433 4434 point = { 4435 position: pathArray[0], 4436 map: map, 4437 clickable: true, 4438 icon: obj.getMarkerImage(), 4439 marker_element: new google.maps.marker.AdvancedMarkerElement({ 4440 position: pathArray[0], 4441 map: map, 4442 content: pin.element 4443 }), 4444 setMap: function (newMap) { 4445 this.map = newMap; 4446 this.marker_element.map = newMap; 4447 }, 4448 getPosition: function () { 4449 return this.position; 4450 }, 4451 setPosition: function (newPosition) { 4452 this.position = newPosition; 4453 this.marker_element.position = newPosition; 4454 } 4455 }; 4456 } 4457 4458 // Select the point 4459 obj.setSelection(point, google.maps.drawing.OverlayType.MARKER); 4460 4461 // Disable the drawing manager 4462 obj.disableDrawingManager(); 4463 4464 // add listener for "selecting" the point 4465 // because that's where we stuff the coordinates into the hidden variable 4466 google.maps.event.addListener(point, 'click', function () { 4467 obj.setSelection(point, google.maps.drawing.OverlayType.MARKER); 4468 }); 4469 } 4470 } 4471 } 4472 } 4473 4474 /** 4475 * Take the given lat and long and try to reverse-geocode to an Address 4476 * then stuff that address as the value of the given element. 4477 */ 4478 this.toAddress = function (latlng, $labelElement) { 4479 // only try if we have a valid latlng 4480 if (!latlng || isNaN(latlng.lat()) || isNaN(latlng.lng())) { 4481 $labelElement.text(''); 4482 return; 4483 } 4484 var geocoder = new google.maps.Geocoder(); 4485 geocoder = geocoder.geocode({ "latLng": latlng }, function (results, status) { 4486 if (status == google.maps.GeocoderStatus.OK) { 4487 if (results[0]) { 4488 $labelElement.text('near ' + results[0].formatted_address); 4489 } 4490 } 4491 else 4492 { 4493 $labelElement.html('<i>selected</i>'); 4494 console.log('Geocoder failed due to: ' + status); 4495 } 4496 }); 4497 } 4498 4499 /** 4500 * Geocode an address to a latLng and center the map on that point. 4501 */ 4502 this.centerMapOnAddress = function () { 4503 var self = this; 4504 // only try if a centerAddress is set 4505 if (!obj.centerAddress) { 4506 return; 4507 } 4508 4509 var geocoder = new google.maps.Geocoder(); 4510 4511 geocoder.geocode({ 'address': obj.centerAddress }, function (results, status) { 4512 if (status == google.maps.GeocoderStatus.OK) { 4513 self.map.setCenter(results[0].geometry.location); 4514 } else { 4515 console.log('Geocode was not successful for the following reason: ' + status); 4516 } 4517 }); 4518 } 4519 4520 /** 4521 * Return a latLng for the "first point" of the selected map. 4522 */ 4523 this.firstPoint = function () { 4524 if (obj.path) { 4525 var coords = obj.path.split('|'); 4526 if (coords) { 4527 var latLng = coords[0].split(','); 4528 var lat = parseFloat(latLng[0]); 4529 var lng = parseFloat(latLng[1]); 4530 return new google.maps.LatLng(lat, lng); 4531 } 4532 } 4533 } 4534 4535 }; // sorta end class 4536 4537 /** 4538 * initialize our event handlers for the button clicks we're going to be handling. 4539 */ 4540 GeoPicker.prototype.initializeEventHandlers = function () { 4541 var controlId = this.controlId, 4542 $control = $('#' + this.controlId), 4543 $hiddenField = $('#' + this.controlId + '_hfGeoPath'), 4544 self = this; 4545 4546 /** 4547 * Toggle the picker on and off when the control's link is clicked. 4548 */ 4549 $('#' + controlId + ' .picker-label').on('click', function (e) { 4550 e.preventDefault(); 4551 var $control = $('#' + controlId); 4552 $(this).toggleClass("active"); 4553 $control.find('.picker-menu').first().toggle(0, function () { 4554 Rock.dialogs.updateModalScrollBar(controlId); 4555 }); 4556 4557 if ( $control.find('.picker-menu').first().is(":visible") ) { 4558 google.maps.event.trigger(self.map, "resize"); 4559 // now we can safely fit the map to any polygon boun
4559dary 4560 self.fitBounds(); 4561 4562 // Scroll down so you can see the done/cancel buttons 4563 $("html,body").animate({ 4564 scrollTop: $control.offset().top 4565 }, 1000); 4566 } 4567 }); 4568 4569 /** 4570 * Hide the expand button if we are in a modal 4571 */ 4572 if ($control.closest('.modal').length) { 4573 $('#btnExpandToggle_' + controlId).hide(); 4574 } 4575 4576 /** 4577 * Handle the toggle expand fullscreen button click. 4578 */ 4579 $('#btnExpandToggle_' + controlId).on('click', function () { 4580 4581 var $myElement = $('#geoPicker_' + self.controlId); 4582 4583 var isExpaned = $myElement.data("fullscreen"); 4584 4585 $(this).children('i').toggleClass("ti-maximize", isExpaned); 4586 $(this).children('i').toggleClass("ti-minimize", ! isExpaned); 4587 4588 // Shrink to regular size 4589 if ( isExpaned ) { 4590 $myElement.data("fullscreen", false); 4591 4592 $(this).closest('.picker-menu').css({ 4593 position: 'absolute', 4594 top: 0, 4595 left: 0, 4596 height: '', 4597 width: 520 4598 }); 4599 // resize the map 4600 $myElement.css({ 4601 height: 300, 4602 width: 500 4603 }); 4604 4605 // move the delete button 4606 $('#gmnoprint-delete-button_' + self.controlId).css({ 4607 left: '200px', 4608 }); 4609 4610 } 4611 else { 4612 4613 // Expand to fullscreen 4614 4615 $myElement.data("fullscreen", true); 4616 // resize the container 4617 $(this).closest('.picker-menu').css({ 4618 position: 'fixed', 4619 top: 0, 4620 left: 0, 4621 height: '100%', 4622 width: '100%' 4623 }); 4624 4625 // resize the map 4626 $myElement.css({ 4627 height: '85%', 4628 width: '100%' 4629 }); 4630 4631 // move the delete button 4632 $('#gmnoprint-delete-button_' + self.controlId).css({ 4633 left: '200px', 4634 }); 4635 } 4636 4637 // tell the map to resize/redraw 4638 google.maps.event.trigger(self.map, 'resize'); 4639 self.fitBounds(); 4640 4641 Rock.dialogs.updateModalScrollBar(self.controlId); 4642 4643 }); 4644 4645 /** 4646 * Handle the Cancel button click by hiding the overlay. 4647 */ 4648 $('#btnCancel_' + controlId).on('click', function () { 4649 $(this).closest('.picker-menu').slideUp(function () { 4650 Rock.dialogs.updateModalScrollBar(controlId); 4651 }); 4652 4653 self.path = self.pathTemp; 4654 4655 if ( self.selectedShape ) { 4656 self.selectedShape.setMap(null); 4657 self.clearSelection(); 4658 4659 // enable the drawing controls again 4660 self.drawingManager.setOptions({ 4661 drawingControlOptions: { 4662 drawingModes: self.getDrawingModes() 4663 } 4664 }); 4665 } 4666 self.plotPath(self.map); 4667 }); 4668 4669 // have the X appear on hover if something is selected 4670 if ($hiddenField.val() && $hiddenField.val() !== '0') { 4671 $control.find('.picker-select-none').addClass('rollover-item').show(); 4672 } 4673 4674 /** 4675 * Handle the Select button click by stuffing the RockGoogleGeoPicker's path value into the hidden field. 4676 */ 4677 $control.find('.js-geopicker-select').on('click', function () {
4678 var geoInput = $('#' + controlId).find('input:checked'), 4679 selectedValue = self.path, 4680 selectedGeographyLabel = $('#selectedGeographyLabel_' + controlId); 4681 4682 $hiddenField.val(self.path); 4683 4684 // have the X appear on hover. something is selected 4685 $control.find('.picker-select-none').addClass('rollover-item'); 4686 $control.find('.picker-select-none').show(); 4687 4688 selectedGeographyLabel.val(selectedValue); 4689 self.toAddress( self.firstPoint(), selectedGeographyLabel); 4690 4691 //clear out any old map positioning 4692 self.initMinMaxLatLng(); 4693 4694 $(this).closest('.picker-menu').slideUp(function () { 4695 Rock.dialogs.updateModalScrollBar(controlId); 4696 }); 4697 4698 $(this).closest('form').submit(); 4699 }); 4700 4701 4702 /** 4703 * Clear the selection when X is clicked 4704 */ 4705 $control.find('.picker-select-none').on('click', function (e) { 4706 e.stopImmediatePropagation(); 4707 var selectedGeographyLabel = $('#selectedGeographyLabel_' + controlId); 4708 $hiddenField.val(""); 4709 4710 // don't have the X appear on hover. nothing is selected 4711 $control.find('.picker-select-none').removeClass('rollover-item'); 4712 $control.find('.picker-select-none').hide(); 4713 4714 selectedGeographyLabel.val(""); 4715 self.toAddress(null, selectedGeographyLabel); 4716 4717 //clear out any old map positioning 4718 self.initMinMaxLatLng(); 4719 4720 return false; 4721 }); 4722 }; 4723 4724 /** 4725 * Initialize the GeoPicker. 4726 */ 4727 GeoPicker.prototype.initialize = function () { 4728 var self = this; 4729 var $hiddenField = $('#' + self.controlId + '_hfGeoPath'); 4730 var deleteButtonId = 'gmnoprint-delete-button_' + self.controlId; 4731 4732 // Pull anything in the hidden field onto this object's path 4733 self.path = $hiddenField.val(); 4734 4735 // store path into pathTemp in case of user cancel. 4736 self.pathTemp = self.path; 4737 4738 // Create a new StyledMapType object, passing it the array of styles, 4739 // as well as the name to be displayed on the map type control. 4740 var styledMap = new google.maps.StyledMapType(self.styles, { name: "Styled Map" }); 4741 4742 // WARNING: I though about removing the "center:" from the options here but then the 4743 // map's controls were different and then our delete button was out of alignment. 4744 var mapOptions = { 4745 center: new google.maps.LatLng( 4746 parseFloat(self.centerLatitude), 4747 parseFloat(self.centerLongitude)), 4748 zoom: 13, 4749 streetViewControl: false, 4750 mapTypeControlOptions: { 4751 mapTypeIds: [google.maps.MapTypeId.ROADMAP, 'map_style'] 4752 }, 4753 }; 4754 4755 if (self.mapId) { 4756 mapOptions.mapId = self.mapId; 4757 } 4758 4759 // center the map on the configured address 4760 self.centerMapOnAddress(); 4761 4762 self.map = new google.maps.Map(document.getElementById('geoPicker_' + self.controlId), mapOptions); 4763 4764 //Associate the styled map with the MapTypeId and set it to display. 4765 self.map.mapTypes.set('map_style', styledMap); 4766 self.map.setMapTypeId('map_style'); 4767 4768 // If we have coordinates we should plot them here... 4769 self.plotPath(self.map); 4770 4771 // Set up the Drawing Manager for creating polygons, circles, etc. 4772 self.drawingManager = new google.maps.drawing.DrawingManager({ 4773 drawingControl: true, 4774 drawingControlOptions: { 4775 drawingModes: self.getDrawingModes() 4776 }, 4777 polygonOptions: { 4778 editable: true, 4779 strokeColor: self.strokeColor, 4780 fillColor: self.fillColor, 4781 strokeWeight: 2 4782 }, 4783 markerOptions: { 4784 icon: self.getMarkerImage() 4785 } 4786 }); 4787 4788 self.drawingManager.setMap(self.map); 4789 4790 // but disable the drawing manager if we already have a point/polygon selected: 4791 if (self.path) { 4792 self.disableDrawingManager(); 4793 } 4794 4795 // Handle when the polygon shape drawing is "complete" 4796 google.maps.event.addListener(self.drawingManager, 'overlaycomplete', function (e) { 4797 if (e.type == google.maps.drawing.OverlayType.POLYGON || e.type == google.maps.drawing.OverlayType.MARKER ) { 4798 // Disable the drawing manager once they've drawn an overlay. 4799 self.disableDrawingManager(); 4800 4801 // Add an event listener that selects the newly-drawn shape when the user mouses down on it. 4802 var newShape = e.overlay; 4803 newShape.type = e.type; 4804 google.maps.event.addListener(newShape, 'click', function () { 4805 self.setSelection(newShape, e.type); 4806 }); 4807 self.setSelection(newShape, e.type); 4808 4809 if (e.type == google.maps.drawing.OverlayType.POLYGON) { 4810 // add listener for moving polygon points. 4811 google.maps.event.addListener(newShape.getPath(), 'set_at', function (e) { 4812 self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON); 4813 }); 4814 4815 // add listener for adding new points. 4816 google.maps.event.addListener(newShape.getPath(), 'insert_at', function (e) { 4817 self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON); 4818 }); 4819 4820 // Add an event listener to implement right-click to delete node 4821 google.maps.event.addListener(newShape, 'rightclick', function (ev) { 4822 if (ev.vertex != null) { 4823 newShape.getPath().removeAt(ev.vertex); 4824 } 4825 self.setSelection(newShape, google.maps.drawing.OverlayType.POLYGON); 4826 }); 4827 } 4828 } 4829 }); 4830 4831 // Clear the current selection when the drawing mode is changed, or when the map is clicked. 4832 google.maps.event.addListener(self.drawingManager, 'drawingmode_changed', self.clearSelection); 4833 google.maps.event.addListener(self.map, 'click', self.clearSelection); 4834 4835 // Move our custom delete button into place once the map is idle. 4836 // as per http://stackoverflow.com/questions/832692/how-to-check-if-google-maps-is-fully-loaded 4837 google.maps.event.addListenerOnce(self.map, 'idle', function () { 4838 $('#' + deleteButtonId).fadeIn(); 4839 4840 // wire up an event handler to the delete button 4841 document.getElementById(deleteButtonId).addEventListener('click', self.deleteSelectedShape); 4842 }); 4843 4844 self.initializeEventHandlers(); 4845 4846 Rock.dialogs.updateModalScrollBar(self.controlId); 4847 }; 4848 4849 var exports = { 4850 googleMapsLoadCallback: function () {
4851 // callback for when google maps api is done loading (if it wasn't loaded already) 4852 $.each(Rock.controls.geoPicker.geoPickerOptions, function (a, options) { 4853 var geoPicker = Rock.controls.geoPicker.geoPickers[options.controlId]; 4854 if (!geoPicker) { 4855 4856 geoPicker = new GeoPicker(options); 4857 Rock.controls.geoPicker.geoPickers[options.controlId] = geoPicker; 4858 geoPicker.initialize(); 4859 } 4860 }); 4861 }, 4862 geoPickers: {}, 4863 geoPickerOptions: {}, 4864 findControl: function (controlId) { 4865 return exports.geoPickers[controlId]; 4866 }, 4867 initialize: function (options) { 4868 if (!options.controlId) throw '`controlId` is required.'; 4869 exports.geoPickerOptions[options.controlId] = options; 4870 4871 $(window).on('googleMapsIsLoaded', this.googleMapsLoadCallback); 4872 4873 // if the google maps api isn't loaded uet, googleMapsLoadCallback will take care of it 4874 if (typeof (google) != "undefined") { 4875 // null it out just in case, to force it to get recreated 4876 exports.geoPickers[options.controlId] = null; 4877 $(window).trigger('googleMapsIsLoaded'); 4878 } 4879 } 4880 }; 4881 4882 return exports; 4883 }()); 4884}(jQuery)); 4885; 4886(function (Sys) { 4887 'use strict'; 4888 Sys.Application.add_load(function () { 4889 $('a.help').on('click', function (e) { 4890 e.preventDefault(); 4891 $(this).siblings('div.alert-info').slideToggle(function () { 4892 Rock.controls.modal.updateSize(this); 4893 }); 4894 $(this).siblings('a.warning').insertAfter($(this)); 4895 $(this).siblings('div.alert-warning').slideUp(); 4896 }); 4897 }); 4898}(Sys)); 4899; 4900(function ($) { 4901 'use strict'; 4902 window.Rock = window.Rock || {}; 4903 4904 Rock.htmlEditor = (function () { 4905 var exports = { 4906 // full list of available items is at http://summernote.org/deep-dive/#custom-toolbar-popover 4907 4908 // the toolbar items to include in the HtmlEditor when Toolbar is set to Light 4909 toolbar_RockCustomConfigLight: 4910 [ 4911 // [groupName, [list of button]] 4912 ['source_group', ['rockcodeeditor']], 4913 ['style_group1', ['bold', 'italic', 'strikethrough', 'link', 'color', 'style', 'ol', 'ul']], 4914 ['style_group3', ['clear']], 4915 ['para', ['paragraph']], 4916 ['plugins1', ['rockmergefield']], 4917 ['plugins2', ['rockimagebrowser', 'rockfilebrowser', 'rockassetmanager']], 4918 ['plugins3', ['rockpastetext', 'rockpastefromword']], 4919 ['style_group2', ['undo', 'redo']] 4920 ], 4921 4922 // the toolbar items to include in the HtmlEditor when Toolbar is set to Full 4923 toolbar_RockCustomConfigFull: 4924 [ 4925 // [groupName, [list of button]] 4926 ['source_group', ['rockcodeeditor']], 4927 ['style_group1', ['bold', 'italic', 'underline', 'strikethrough', 'ol', 'ul', 'link']], 4928 ['style_group2', ['undo', 'redo']], 4929 ['style_group3', ['clear']], 4930 ['style_group4', ['style', 'color']], 4931 ['full_toolbar_only', ['fontname', 'fontsize', 'superscript', 'subscript', 'table', 'hr']], 4932 ['para', ['paragraph']], 4933 ['plugins1', ['rockmergefield']], 4934 ['plugins2', ['rockimagebrowser', 'rockfilebrowser', 'rockassetmanager']], 4935 ['plugins3', ['rockpastetext', 'rockpastefromword']], 4936 ['help_group1', ['help']] 4937 ] 4938 }; 4939 4940 return exports; 4941 4942 }()); 4943}(jQuery)); 4944; 4945(function ($) { 4946 'use strict'; 4947 window.Rock = window.Rock || {}; 4948 Rock.controls = Rock.controls || {}; 4949 4950 Rock.controls.imageUploader = (function () { 4951 var _configure = function (options) { 4952 options.isBinaryFile = options.isBinaryFile || 'T'; 4953 4954 var $contentFileSource = $('#' + options.hfContentFileSource); 4955 var $binaryFileId = $('#' + options.hfFileId); 4956 4957 // default setImageUrlOnUpload to true if not specified 4958 if (options.setImageUrlOnUpload == null) { 4959 options.setImageUrlOnUpload = true; 4960 } 4961 4962 var wsUrl = Rock.settings.get('baseUrl') 4963 + 'ImageUploader.ashx?' 4964 + 'isBinaryFile=' + options.isBinaryFile; 4965 4966 if (options.isBinaryFile == 'T') { 4967 wsUrl += '&fileId=' + options.fileId 4968 + '&fileTypeGuid=' + options.fileTypeGuid; 4969 } 4970 else { 4971 // note rootFolder is encrypted to prevent direct access to filesystem via the URL 4972 wsUrl += '&rootFolder=' + (encodeURIComponent(options.rootFolder) || ''); 4973 } 4974 4975 if (options.isTemporary == 'F') { 4976 wsUrl += '&IsTemporary=False'; 4977 } 4978 4979 // uses https://github.com/blueimp/jQuery-File-Upload 4980 $('#' + options.controlId).fileupload({ 4981 url: wsUrl, 4982 dataType: 'json', 4983 dropZone: $('#' + options.controlId).closest('.imageupload-dropzone'), 4984 autoUpload: true, 4985 submit: options.submitFunction, 4986 start: function (e, data) { 4987 var $el = $('#' + options.controlId).closest('.imageupload-group'); 4988 $el.find('.js-upload-progress').rockFadeIn(); 4989 }, 4990 progressall: function (e, data) { 4991 // var $el = $('#' + options.controlId).closest('.imageupload-group'); 4992 // implement this to show progress percentage 4993 }, 4994 stop: function (e) { 4995 var $el = $('#' + options.controlId).closest('.imageupload-group'); 4996 $el.find('.js-upload-progress').hide(); 4997 $el.find('.imageupload-dropzone').rockFadeIn(); 4998 }, 4999 done: function (e, data) { 5000 var $el = $('#' + options.imgThumbnail); 5001 5002 if ((options.isBinaryFile || 'T') == 'F') { 5003 $contentFileSource.val(data.response().result.FileName); 5004 } 5005 else { 5006 $binaryFileId.val(data.response().result.Id); 5007 } 5008 5009 var getImageUrl = Rock.settings.get('baseUrl') 5010 + 'GetImage.ashx?' 5011 + 'isBinaryFile=' + (options.isBinaryFile || 'T'); 5012 5013 if (options.disablePredictableIds) { 5014 getImageUrl += '&guid=' + data.response().result.Guid 5015 } 5016 else { 5017 getImageUrl += '&id=' + data.response().result.Id; 5018 } 5019 5020 getImageUrl += '&fileName=' + data.response().result.FileName 5021 + '&width=500'; 5022 5023 if (options.rootFolder) { 5024 // note rootFolder is encrypted to prevent direct access to filesystem via the URL 5025 getImageUrl += '&rootFolder=' + encodeURIComponent(options.rootFolder); 5026 } 5027 5028 if (options.setImageUrlOnUpload) {
5029 if ($el.is('img')) { 5030 $el.attr('src', getImageUrl); 5031 } 5032 else { 5033 $el.attr('style', 'background-image:url("' + getImageUrl + '");background-size:cover;background-position:50%'); 5034 } 5035 } 5036 5037 $('#' + options.aRemove).show(); 5038 if (options.postbackScript) { 5039 window.location = "javascript:" + options.postbackScript; 5040 } 5041 5042 if (options.doneFunction) { 5043 options.doneFunction(e, data); 5044 } 5045 }, 5046 fail: function (e, data) { 5047 var $el = $('#' + options.controlId).closest('.imageupload-group'); 5048 $el.siblings('.js-rockupload-alert').remove(); 5049 var $warning = $('<div class="alert alert-warning alert-dismissable js-rockupload-alert"/>'); 5050 5051 var msg = "unable to upload"; 5052 if (data.response().jqXHR && data.response().jqXHR.status == 406) { 5053 msg = "file type not allowed"; 5054 } else if (data.response().jqXHR && data.response().jqXHR.responseText) { 5055 msg = data.response().jqXHR.responseText; 5056 } 5057 5058 if (options.maxUploadBytes && data.total) { 5059 if (data.total >= options.maxUploadBytes) { 5060 msg = "file size is limited to " + (options.maxUploadBytes / 1024 / 1024) + "MB"; 5061 } 5062 } 5063 5064 $warning.append('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>') 5065 .append('<strong><i class="ti ti-alert-triangle"></i> Warning </strong>') 5066 .append(msg); 5067 $warning.insertBefore($el); 5068 } 5069 }); 5070 5071 $('#' + options.aRemove).on('click', function () { 5072 $(this).hide(); 5073 var $el = $('#' + options.imgThumbnail); 5074 var noPictureUrl = options.noPictureUrl || Rock.settings.get('baseUrl') + 'Assets/Images/no-picture.svg'; 5075 if ($el.is('img')) { 5076 $el.attr('src', noPictureUrl); 5077 } 5078 else { 5079 $el.attr('style', 'background-image:url(' + noPictureUrl + ');background-size:cover;background-position:50%'); 5080 } 5081 5082 if (options.deleteFunction) { 5083 options.deleteFunction(); 5084 } 5085 5086 if (options.postbackRemovedScript) { 5087 window.location = "javascript:" + options.postbackRemovedScript; 5088 } else { 5089 $binaryFileId.val('0'); 5090 $contentFileSource.val(''); 5091 } 5092 return false; 5093 }); 5094 }, 5095 exports = { 5096 initialize: function (options) { 5097 if (!options.controlId) throw 'Control ID must be set.'; 5098 _configure(options); 5099 } 5100 }; 5101 5102 return exports; 5103 }()); 5104}(jQuery)); 5105; 5106(function ($) { 5107 'use strict'; 5108 window.Rock = window.Rock || {}; 5109 Rock.controls = Rock.controls || {}; 5110 5111 Rock.controls.itemPicker = (function () { 5112 var ItemPicker = function (options) { 5113 this.options = options; 5114 5115 // set a flag so that the picker only auto-scrolls to a selected item once. This prevents it from scrolling at unwanted times 5116 this.alreadyScrolledToSelected = false; 5117 this.iScroll = null; 5118 }, 5119 exports; 5120 5121 ItemPicker.prototype = { 5122 constructor: ItemPicker, 5123 initialize: function () { 5124 var $control = $('#' + this.options.controlId), 5125 $tree = $control.find('.treeview'), 5126 treeOptions = { 5127 multiselect: this.options.allowMultiSelect, 5128 categorySelection: this.options.allowCategorySelection, 5129 categoryPrefix: this.options.categoryPrefix, 5130 restUrl: this.options.restUrl, 5131 context: this.options.context,
5132 restParams: this.options.restParams, 5133 expandedIds: this.options.expandedIds, 5134 expandedCategoryIds: this.options.expandedCategoryIds, 5135 showSelectChildren: this.options.showSelectChildren, 5136 id: this.options.startingId 5137 }, 5138 $hfItemIds = $control.find('.js-item-id-value'), 5139 $hfExpandedIds = $control.find('.js-initial-item-parent-ids-value'), 5140 $hfExpandedCategoryIds = $control.find('.js-expanded-category-ids'); 5141 5142 if (typeof this.options.mapItems === 'function') { 5143 treeOptions.mapping = { 5144 mapData: this.options.mapItems 5145 }; 5146 } 5147 5148 // clean up the tree (in case it was initialized already, but we are rebuilding it) 5149 var rockTree = $tree.data('rockTree'); 5150 if (rockTree) { 5151 rockTree.nodes = []; 5152 } 5153 $tree.empty(); 5154 5155 var $scrollContainer = $control.find('.scroll-container .viewport'); 5156 var $scrollIndicator = $control.find('.track'); 5157 this.iScroll = new IScroll($scrollContainer[0], { 5158 mouseWheel: true, 5159 indicators: { 5160 el: $scrollIndicator[0], 5161 interactive: true, 5162 resize: false, 5163 listenY: true, 5164 listenX: false, 5165 }, 5166 click: false, 5167 preventDefaultException: { tagName: /.*/ } 5168 }); 5169 5170 // Since some handlers are "live" events, they need to be bound before tree is initialized 5171 this.initializeEventHandlers(); 5172 5173 if ($hfItemIds.val() && $hfItemIds !== '0') { 5174 treeOptions.selectedIds = $hfItemIds.val().split(','); 5175 } 5176 5177 if ($hfExpandedIds.val()) { 5178 treeOptions.expandedIds = $hfExpandedIds.val().split(','); 5179 } 5180 5181 if ($hfExpandedCategoryIds.val()) { 5182 treeOptions.expandedCategoryIds = $hfExpandedCategoryIds.val().split(','); 5183 } 5184 5185 if (this.options.universalItemPicker) { 5186 function mapUniversalItems(data) { 5187 return data.map(item => { 5188 const treeItem = { 5189 id: item.value, 5190 name: item.text, 5191 iconCssClass: item.iconCssClass, 5192 hasChildren: item.hasChildren, 5193 isCategory: false, 5194 isSelectionDisabled: item.isFolder, 5195 childrenUrl: item.childrenUrl 5196 }; 5197 5198 if (Array.isArray(item.children)) { 5199 treeItem.children = mapUniversalItems(item.children); 5200 } 5201 5202 return treeItem; 5203 }); 5204 } 5205 5206 treeOptions.universalItemPicker = true; 5207 treeOptions.expandedIds = (treeOptions.expandedIds || []).filter(id => id !== 0); 5208 5209 treeOptions.getNodes = (parentId, parentNode, selectedIds, toExpandIds) => { 5210 const req = { 5211 context: treeOptions.context 5212 }; 5213 5214 if (!parentId) { 5215 req.expandToValues = (selectedIds || []).filter(a => a !== "0"); 5216 } 5217 else { 5218 req.parentValue = parentId; 5219 } 5220 5221 return $.ajax({ 5222 method: 'POST', 5223 data: JSON.stringify(req), 5224 url: treeOptions.restUrl, 5225 dataType: 'json', 5226 contentType: 'application/json' 5227 }) 5228 .then(data => { 5229 function checkItemsForExpansion(items, path) { 5230 for (let i = 0; i < items.length; i++) { 5231 if (selectedIds.some(id => id === items[i].value)) { 5232 for (let p = 0; p < path.length; p++) { 5233 if (!toExpandIds.includes(path[p])) { 5234 toExpandIds.push(path[p]); 5235 } 5236 } 5237 } 5238 5239 if (Array.isArray(items[i].children)) { 5240 checkItemsForExpansion(items[i].children, [...path, items[i].value]); 5241 } 5242 } 5243 } 5244 5245 checkItemsForExpansion(data, []); 5246 5247 return data; 5248 }); 5249 }; 5250 5251 treeOptions.mapping = { 5252 mapData: mapUniversalItems 5253 }; 5254 } 5255 5256 $tree.rockTree(treeOptions); 5257 this.updateScrollbar(); 5258 }, 5259 initializeEventHandlers: function () { 5260 var self = this, 5261 $control = $('#' + this.options.controlId), 5262 $spanNames = $control.find('.selected-names'), 5263 $hfItemIds = $control.find('.js-item-id-value'), 5264 $hfItemNames = $control.find('.js-item-name-value'); 5265 5266 // Bind tree events 5267 $control.find('.treeview') 5268 .on('rockTree:selected', function () { 5269 // intentionally blank 5270 }) 5271 .on('rockTree:itemClicked', function (e) { 5272 // make sure it doesn't autoscroll after something has been manually clicked 5273 self.alreadyScrolledToSelected = true; 5274 if (!self.options.allowMultiSelect) { 5275 $control.find('.picker-btn').trigger('click'); 5276 } 5277 }) 5278 .on('rockTree:expand rockTree:collapse rockTree:dataBound', function (evt) { 5279 self.updateScrollbar(); 5280 }) 5281 .on('rockTree:rendered', function (evt) { 5282 self.scrollToSelectedItem(); 5283 }); 5284 5285 $control.find('.picker-label').on('click', function (e) { 5286 e.preventDefault(); 5287 $(this).toggleClass("active"); 5288 $control.find('.picker-menu').first().toggle(0, function () { 5289 self.scrollToSelectedItem(); 5290 }); 5291 }); 5292 5293 $control.find('.picker-cancel').on('click', function () { 5294 $(this).toggleClass("active"); 5295 $(this).closest('.picker-menu').toggle(0, function () { 5296 self.updateScrollbar(); 5297 }); 5298 $(this).closest('.picker-label').toggleClass("active"); 5299 }); 5300 5301 // have the X appear on hover if something is selected 5302 if ($hfItemIds.val() && $hfItemIds.val() !== '0') { 5303 $control.find('.picker-select-none').addClass('rollover-item'); 5304 $control.find('.picker-select-none').show(); 5305 } 5306 5307 $control.find('.picker-btn').on('click', function (el) { 5308 5309 var rockTree = $control.find('.treeview').data('rockTree'), 5310 selectedNodes = rockTree.selectedNodes, 5311 selectedIds = [], 5312 selectedNames = []; 5313 $.each(selectedNodes, function (index, node) { 5314 var nodeName = $("<textarea/>").html(node.name).text(); 5315 selectedNames.push(nodeName); 5316 if (!selectedIds.includes(node.id)) { 5317 selectedIds.push(node.id); 5318 } 5319 }); 5320 5321 $hfItemIds.val(selectedIds.join(',')).trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field. 5322 $hfItemNames.val(selectedNames.join(',')); 5323 5324 // have the X appear on hover. something is selected 5325 $control.find('.picker-select-none').addClass('rollover-item'); 5326 $control.find('.picker-select-none').show(); 5327 5328 $spanNames.text(selectedNames.join(', ')); 5329 $spanNames.attr('title', $spanNames.text()); 5330 5331 $(this).closest('.picker-label').toggleClass("active"); 5332 $(this).closest('.picker-menu').toggle(0, function () { 5333 self.updateScrollbar(); 5334 }); 5335 5336 $(this).trigger('onclick'); 5337 if (!(el && el.originalEvent && el.originalEvent.srcElement == this)) { 5338 // if this event was called by something other than the button itself, make sure the execute the href (which is probably javascript) 5339 var jsPostback = $(this).attr('href'); 5340 if (jsPostback) { 5341 window.location = jsPostback; 5342 } 5343 } 5344 }); 5345 5346 $control.find('.picker-select-none').on("click", function (e) { 5347 e.preventDefault(); 5348 e.stopImmediatePropagation(); 5349 5350 var rockTree = $control.find('.treeview').data('rockTree'); 5351 rockTree.clear(); 5352 $hfItemIds.val('0').trigger('change'); // .trigger('change') is used to cause jQuery to fire any "onchange" event handlers for this hidden field. 5353 $hfItemNames.val(''); 5354 5355 // don't have the X appear on hover. nothing is selected 5356 $control.find('.picker-select-none').removeClass('rollover-item').hide(); 5357 5358 $control.siblings('.js-hide-on-select-none').hide(); 5359 5360 $spanNames.text(self.options.defaultText); 5361 $spanNames.attr('title', $spanNames.text()); 5362 $(this).trigger('onclick'); 5363 }); 5364 5365 // clicking on the 'select all' btn 5366 $control.on('click', '.js-select-all', function (e) 5367 { 5368 var rockTree = $control.find('.treeview').data('rockTree'); 5369 5370 e.preventDefault(); 5371 e.stopPropagation(); 5372 5373 var $itemNameNodes = rockTree.$el.find('.rocktree-name'); 5374 5375 var allItemNodesAlreadySelected = true; 5376 $itemNameNodes.each(function (a) 5377 { 5378 if (!$(this).hasClass('selected')) { 5379 allItemNodesAlreadySelected = false; 5380 } 5381 }); 5382 5383 if (!allItemNodesAlreadySelected) { 5384 // mark them all as unselected (just in case some are selected already), then click them to select them 5385 $itemNameNodes.removeClass('selected'); 5386 $itemNameNodes.trigger('click'); 5387 } else { 5388 // if all were already selected, toggle them to unselected 5389 rockTree.setSelected([]); 5390 $itemNameNodes.removeClass('selected'); 5391 } 5392 }); 5393 }, 5394 updateScrollbar: function (sPosition) { 5395 var self = this; 5396 // first, update this control's scrollbar, then the modal'
5396s 5397 var $container = $('#' + this.options.controlId).find('.scroll-container'); 5398 5399 if ($container.is(':visible')) { 5400 if (!sPosition) { 5401 sPosition = 'relative' 5402 } 5403 if (self.iScroll) { 5404 self.iScroll.refresh(); 5405 } 5406 } 5407 5408 // update the outer modal 5409 Rock.dialogs.updateModalScrollBar(this.options.controlId); 5410 }, 5411 scrollToSelectedItem: function () { 5412 var $selectedItem = $('#' + this.options.controlId).find('.picker-menu').find('.selected').first(); 5413 if ($selectedItem.length && (!this.alreadyScrolledToSelected)) { 5414 this.updateScrollbar(); 5415 this.iScroll.scrollToElement('.selected', '0s'); 5416 this.alreadyScrolledToSelected = true; 5417 } else { 5418 // initialize/update the scrollbar 5419 this.updateScrollbar(); 5420 } 5421 } 5422 }; 5423 5424 exports = { 5425 defaults: { 5426 id: 0, 5427 controlId: null, 5428 universalItemPicker: false, 5429 restUrl: null, 5430 restParams: null, 5431 allowCategorySelection: false, 5432 categoryPrefix: '', 5433 allowMultiSelect: false, 5434 defaultText: '', 5435 selectedIds: null, 5436 expandedIds: null, 5437 expandedCategoryIds: null, 5438 showSelectChildren: false 5439 }, 5440 controls: {}, 5441 initialize: function (options) { 5442 var settings, 5443 itemPicker; 5444 5445 if (!options.controlId) throw 'controlId must be set'; 5446 if (!options.restUrl) throw 'restUrl must be set'; 5447 5448 settings = $.extend({}, exports.defaults, options); 5449 5450 if (!settings.defaultText) { 5451 settings.defaultText = exports.defaults.defaultText; 5452 } 5453 5454 itemPicker = new ItemPicker(settings); 5455 exports.controls[settings.controlId] = itemPicker; 5456 itemPicker.initialize(); 5457 } 5458 }; 5459 5460 return exports; 5461 }()); 5462}(jQuery)); 5463; 5464; (function () { 5465 function updateKeyValues(e) { 5466 var $span = e.closest('span.key-value-list'); 5467 var newValue = ''; 5468 5469 $span.children('span.key-value-rows:first').children('div.controls-row').each(function (index) { 5470 if (newValue !== '') { 5471 newValue += '|'; 5472 } 5473 5474 var keyValue = $(this).children('.key-value-key').first().val(); 5475 var valueValue = $(this).children('.key-value-value').first().val(); 5476 5477 // if the key or value have any magic chars ('^' or '|' or ','' ), URI encode each magic char using encodeURIComponent so that commas also get encoded 5478 var keyValueDelimiters = ['^', '|', ',']; 5479 keyValueDelimiters.forEach(function (v, i, a) { 5480 var re = new RegExp('\\' + v, 'g'); 5481 if (keyValue.indexOf(v) > -1) { 5482 keyValue = keyValue.replace(re, encodeURIComponent(v)); 5483 } 5484 if (valueValue.indexOf(v) > -1) { 5485 valueValue = valueValue.replace(re, encodeURIComponent(v)); 5486 } 5487 }); 5488 5489 newValue += keyValue + '^' + valueValue; 5490 }); 5491 $span.children('input').first().val(newValue); 5492 5493 raiseKeyValueList(); 5494 } 5495 5496 function removeKeyValue(e) { 5497 var $rows = $(e).closest('span.key-value-rows'); 5498 $(e).closest('div.controls-row').remove(); 5499 Rock.controls.modal.updateSize($(e)); 5500 updateKeyValues($rows); 5501 } 5502 5503 Sys.Application.add_load(function () { 5504 5505 $('a.key-value-add').on('click', function (e) { 5506 e.preventDefault(); 5507 var $keyValueList = $(this).closest('.key-value-list'); 5508 $keyValueList.find('.key-value-rows').append($keyValueList.find('.js-value-html').val()); 5509 Rock.controls.modal.updateSize($(this)); 5510 $('a.key-value-remove').off('click'); 5511 $('a.key-value-remove').on('click', function (e) { 5512 e.preventDefault(); 5513 removeKeyValue(this); 5514 }); 5515 $('.js-key-value-input').off('change'); 5516 $('.js-key-value-input').on('change', function (e) { 5517 e.preventDefault(); 5518 updateKeyValues($(this)); 5519 }); 5520 updateKeyValues($(this)); 5521 }); 5522 5523 $('a.key-value-remove').off('click'); 5524 $('a.key-value-remove').on('click', function (e) { 5525 e.preventDefault(); 5526 removeKeyValue(this); 5527 }); 5528 5529 $('.js-key-value-input').off('change'); 5530 $('.js-key-value-input').on('change', function (e) { 5531 e.preventDefault(); 5532 updateKeyValues($(this)); 5533 }); 5534 });
5535})(); 5536; 5537(function ($) { 5538 'use strict'; 5539 window.Rock = window.Rock || {}; 5540 Rock.controls = Rock.controls || {}; 5541 5542 Rock.controls.listItems = (function () { 5543 var exports = { 5544 5545 initialize: function (options) { 5546 if (!options.id) { 5547 throw 'id is required'; 5548 } 5549 5550 if (!options.clientId) { 5551 throw 'clientId is required'; 5552 } 5553 5554 const $listItemsControl = $('#' + options.clientId); 5555 5556 function updateListItemValues($e) { 5557 var $span = $e.closest('span.list-items'); 5558 var keyValuePairs = []; 5559 $span.children('span.list-items-rows').first().children('div.controls-row').each(function (index) { 5560 keyValuePairs.push({ 5561 'Key': $(this).children('.input-group').find('.js-list-items-input').first().data('id'), 5562 'Value': $(this).children('.input-group').find('.js-list-items-input').first().val() 5563 }); 5564 }); 5565 $span.children('input').first().val(JSON.stringify(keyValuePairs)); 5566 if (options.valueChangedScript) { 5567 window.location = "javascript:" + options.valueChangedScript; 5568 } 5569 5570 } 5571 5572 var fixHelper = function (e, ui) { 5573 ui.children().each(function () { 5574 $(this).width($(this).width()); 5575 }); 5576 return ui; 5577 }; 5578 5579 $listItemsControl.find('a.list-items-add').on('click', function (e) { 5580 e.preventDefault(); 5581 $listItemsControl.find('.list-items-rows').append($listItemsControl.find('.js-list-items-html').val()); 5582 updateListItemValues($(this)); 5583 Rock.controls.modal.updateSize($(this)); 5584 }); 5585 5586 // Use add_load to fire with page load and postbacks 5587 Sys.Application.add_load(function () { 5588 function onRemoveClick(event) { 5589 event.preventDefault(); 5590 var $rows = $(this).closest('span.list-items-rows'); 5591 $(this).closest('div.controls-row').remove(); 5592 updateListItemValues($rows); 5593 Rock.controls.modal.updateSize($(this)); 5594 } 5595 5596 function onFocus(event) { 5597 var element = event.target; 5598 var $element = $(element); 5599 var valueOnFocus = element.value; 5600 5601 function onBlur() { 5602 var valueOnBlur = element.value; 5603 5604 if (valueOnFocus != valueOnBlur) { 5605 updateListItemValues($element); 5606 } 5607 } 5608 5609 // Only handle the focus out event once and remove the handler. 5610 $element.one('blur', onBlur); 5611 } 5612 5613 $listItemsControl.find('a.list-items-remove').off('click', onRemoveClick).on('click', onRemoveClick); 5614 $listItemsControl.find('.js-list-items-input').off('focus', onFocus).on('focus', onFocus); 5615 }); 5616 5617 $listItemsControl.find('.list-items-rows').sortable({ 5618 helper: fixHelper, 5619 handle: '.ti-menu-2', 5620 start: function (event, ui) { 5621 { 5622 var start_pos = ui.item.index(); 5623 ui.item.data('start_pos', start_pos); 5624 } 5625 }, 5626 update: function (event, ui) { 5627 updateListItemValues($(this)); 5628 } 5629 5630 }).disableSelection(); 5631 5632 } 5633 }; 5634 5635 return exports; 5636 }()); 5637}(jQuery)); 5638; 5639(function($) { 5640 window.Rock = window.Rock || {}; 5641 window.Rock.controls = window.Rock.controls || {}; 5642 5643 window.Rock.controls.mediaElementPicker = (function () { 5644 var exports = { 5645 initialize: function(options) { 5646 var $picker = $('#' + options.controlId); 5647 5648 $picker.data('required', options.required === true); 5649 5650 // Setup click handlers for any refresh buttons. 5651 $picker.find('.js-media-element-picker-refresh').on('click', function () { 5652 if ($(this).hasClass('disabled')) { 5653 return false; 5654 } 5655 5656 $picker.find('.js-media-element-picker-refresh').addClass('disabled'); 5657 $(this).find('i.ti').addClass('ti-spin'); 5658
5659 window.location = 'javascript:' + options.refreshScript; 5660 5661 return false; 5662 }); 5663 5664 // Re-validate after they change the selection of the media. 5665 $picker.find('.js-media-element-value select').on('change', function () { 5666 ValidatorValidate(window[$picker.find('.js-media-element-validator').prop('id')]); 5667 }); 5668 }, 5669 5670 clientValidate: function (validator, args) { 5671 // After a postback, the validator is not actually in the DOM 5672 // anymore. So find the fake ancestor's Id and then lookup the 5673 // real media element picker. 5674 var mediaElementPickerId = $(validator).closest('.js-media-element-picker').attr('id'); 5675 var $mediaElementPicker = $('#' + mediaElementPickerId); 5676 5677 var required = $mediaElementPicker.data('required') === true; 5678 var value = $mediaElementPicker.find('.js-media-element-value select').val() || ''; 5679 var isValid = !required || (value !== '' && value !== '0'); 5680 5681 if (isValid) { 5682 $mediaElementPicker.closest('.form-group').removeClass('has-error'); 5683 args.IsValid = true; 5684 } 5685 else { 5686 $mediaElementPicker.closest('.form-group').addClass('has-error'); 5687 args.IsValid = false; 5688 } 5689 } 5690 }; 5691 5692 return exports; 5693 })(); 5694})(jQuery); 5695; 5696(function ($) { 5697 'use strict'; 5698 window.Rock = window.Rock || {}; 5699 Rock.controls = Rock.controls || {}; 5700 5701 Rock.controls.mediaPlayer = (function () { 5702 var exports = { 5703 initialize: function () { 5704 5705 Sys.Application.add_load(function () { 5706 var cssFile = Rock.settings.get('baseUrl') + 'Scripts/mediaelementjs/mediaelementplayer.min.css'; 5707 var jsFile = Rock.settings.get('baseUrl') + 'Scripts/mediaelementjs/mediaelement-and-player.js'; 5708 5709 // ensure that css for mediaelementplayers is added to page 5710 if (!$('#mediaElementCss').length) { 5711 $('head').append("<link id='mediaElementCss' href='" + cssFile + "' type='text/css' rel='stylesheet' />"); 5712 } 5713 5714 // ensure that js for mediaelementplayers is added to page 5715 if (!$('#mediaElementJs').length) { 5716 // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this 5717 $.ajaxSetup({ cache: true }); 5718 $('head').prepend("<script id='mediaElementJs' src='" + jsFile + "' />"); 5719 } 5720 5721 // 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) 5722 $('audio.js-media-audio,video.js-media-video').mediaelementplayer({ enableAutosize: true }); 5723 }); 5724 5725 } 5726 }; 5727 5728 return exports; 5729 }()); 5730 5731 /** This is the new Rock media player that uses Plyr. */ 5732 Rock.controls.mediaplayer = (function () { 5733 var exports = { 5734 initialize: function (options) { 5735 if (!options.id) { 5736 throw "id is required"; 5737 } 5738 5739 if (!options.playerId) { 5740 throw "playerId is required"; 5741 } 5742 5743 if (!options.progressId) { 5744 throw "progressId is required"; 5745 } 5746 5747 var $playerElement = $("#" + options.id); 5748 var playerOptions = $playerElement.data("player-options"); 5749 5750 var player = new Rock.UI.MediaPlayer("#" + options.playerId, playerOptions); 5751 5752 player.on("ready", function () { 5753 $("#" + options.progressId).val(player.percentWatched); 5754 }); 5755 5756 player.on("progress", function () { 5757 console.log("progress", player.percentWatched, options.progressId); 5758 $("#" + options.progressId).val(player.percentWatched); 5759 }); 5760 5761 $playerElement 5762 .data("required", options.required || false) 5763 .data("progress-id", options.progressId) 5764 .data("required-percentage", options.requiredPercentage || 0) 5765 .data("required-error-message", options.requiredErrorMessage || "You must watch the video."); 5766 }, 5767 5768 clientValidate: function (validator, args) {
5769 var $playerElement = $(validator).closest(".js-media-player"); 5770 var $formGroup = $playerElement.parent().hasClass("control-wrapper") ? $playerElement.closest(".form-group") : null; 5771 var progressId = $playerElement.data("progress-id"); 5772 var required = $playerElement.data('required'); 5773 var requiredProgress = $playerElement.data("required-percentage"); 5774 var progress = parseFloat($("#" + progressId).val() || "0"); 5775 5776 var isValid = !required || progress >= requiredProgress; 5777 5778 if (isValid) { 5779 if ($formGroup) { 5780 $formGroup.removeClass("has-error"); 5781 } 5782 5783 args.IsValid = true; 5784 } 5785 else { 5786 if ($formGroup) { 5787 $formGroup.addClass("has-error"); 5788 } 5789 5790 args.IsValid = false; 5791 validator.errormessage = $playerElement.data('required-error-message'); 5792 } 5793 } 5794 }; 5795 5796 return exports; 5797 }()); 5798}(jQuery)); 5799; 5800(function ($) { 5801 'use strict'; 5802 window.Rock = window.Rock || {}; 5803 Rock.controls = Rock.controls || {}; 5804 5805 Rock.controls.mediaSelector = (function () { 5806 5807 var exports = { 5808 clientValidate: function (validator, args) { 5809 var $checkBoxList = $(validator).closest('.js-mediaselector'); 5810 5811 var checkboxes = $checkBoxList.find('input'); 5812 var isValid = false; 5813 for (var i = 0; i < checkboxes.length; i++) { 5814 if (checkboxes[i].checked) { 5815 isValid = true; 5816 break; 5817 } 5818 } 5819 5820 if (isValid) { 5821 $checkBoxList.removeClass('has-error'); 5822 } else { 5823 $checkBoxList.addClass('has-error'); 5824 } 5825 5826 args.IsValid = isValid; 5827 } 5828 }; 5829 5830 return exports; 5831 }()); 5832}(jQuery)); 5833 5834 5835; 5836/* 5837 * BJW 7-7-2020 5838 * Previous Issues and Fixes 5839 * 5840 * April 15, 2020 5841 * Problem: 5842 * Open a modal dialog and close it. Next, try to open a modal alert. An invisible backdrop is added to the 5843 * page such that the page cannot be interacted with. The alert does not appear. 5844 * Solution: 5845 * Every postback causes the C# modal control code to register JS that checks a hidden field to determine 5846 * if a modal should be open or closed. If many modals exist, then they are all checked and all call 5847 * the JS close dialog method, which removed backdrops. If each modal removes any backdrop without 5848 * discretion, then they interfere with each other. Therefore, only remove backdrops that belong to the modal 5849 * in current context being closed. 5850 * https://github.com/SparkDevNetwork/Rock/commit/8a1c5653c534c61548de34d0ca1a4483304437a1 5851 * 5852 * June 1, 2020 5853 * Problem: 5854 * When a modal is opened, sometimes the body is set to .modal-open and sometimes a manager (usually the 5855 * update panel of the block) is set to .modal-open. In the event of the manager, cleanup was not occurring 5856 * and the class remained even after the modal was gone. 5857 * Solution: 5858 * A param was added to the close dialog JS method that allowed proper cleanup of classes (.modal-open) added 5859 * to the manager. 5860 * https://github.com/SparkDevNetwork/Rock/commit/26cf101ae8e8923008ae42f3d833cab5a35df8cb 5861 * 5862 * July 1, 2020 5863 * Problem: 5864 * Open a dialog. Close the dialog with a postback that calls C# Hide with no manager param. Open the dialog again 5865 * and there is no backdrop. 5866 * Solution: 5867 * This seems to be an issue with bootstrap (seems unlikely) or the combination of bootstrap with the postback 5868 * UI re-rendering. The modal is removed from the DOM before our JS close method is reached. Therefore proper 5869 * cleanup of the first modal open is never done. This seems to impact the second opening and bootstrap fails 5870 * to add the backdrop. Thus our code adds a backdrop if one is not visible. 5871 * https://github.com/SparkDevNetwork/Rock/commit/f08bb6fc1e17b79d0772152707acd4a3876a68d6 5872 * 5873 * July 7, 2020 5874 * Problem: 5875 * Open a dialog. Close the dialog with a postback that calls C# Hide with no manager param. Open the dialog again 5876 * and there is a backdrop but no modal. 5877 * Solution: 5878 * This is because the postback calls the C# Hide method, which does not provide the manager to the JS close modal 5879 * method. Because of this, proper cleanup doesn't occur. We can calculate the manger as we did when we opened the 5880 * modal, which fixes the issue. 5881 * Also, there was a bug in hiding the backdrop where if the owner no longer existed 5882 * in the DOM, then the backdrop was not removed. 5883 */ 5884 5885(function ($) { 5886 'use strict'; 5887 window.Rock = window.Rock || {}; 5888 Rock.controls = Rock.controls || {}; 5889 5890 Rock.controls.modal = (function () { 5891 // shows the IFrame #modal-popup modal 5892 var _showModalPopup = function (sender, popupUrl) {
5893 var $modalPopup = $('#modal-popup'); 5894 var $modalPopupIFrame = $modalPopup.find('iframe'); 5895 5896 // Use the anchor tag's title attribute as the title of the dialog box 5897 if (sender && sender.attr('title') != undefined) { 5898 $('#modal-popup_panel h3').html(sender.attr('title') + ' <small></small>'); 5899 } 5900 5901 $modalPopupIFrame.height('auto'); 5902 5903 $modalPopupIFrame.one('load', function () { 5904 5905 // now that the iframe is loaded, show it, set it's initial height and do a modal layout 5906 $('#modal-popup').fadeTo(0, 1); 5907 5908 var newHeight = $(this.contentWindow.document).height(); 5909 if ($(this).height() != newHeight) { 5910 $(this).height(newHeight); 5911 } 5912 5913 $('body').addClass('modal-open'); 5914 if (document.body.clientHeight > window.innerHeight) { 5915 $('body').css('padding-right', Rock.controls.util.getScrollbarWidth()); 5916 } 5917 $('#modal-popup').modal('layout'); 5918 }); 5919
5920 // Use the anchor tag's href attribute as the source for the iframe 5921 // this will trigger the load event (above) which will show the popup 5922 $('#modal-popup').fadeTo(0, 0); 5923 5924 // Indicate to Rock that this content is being served in an iFrame modal. 5925 var separator = popupUrl.indexOf("?") === -1 ? "?" : "&"; 5926 popupUrl = popupUrl + separator + "IsIFrameModal=true"; 5927 5928 $modalPopupIFrame.attr('src', popupUrl); 5929 $('#modal-popup').modal({ 5930 show: true, 5931 backdrop: 'static', 5932 keyboard: false, 5933 attentionAnimation: '', 5934 modalOverflow: true 5935 }); 5936 } 5937 5938 // Closes the non-iframe modal dialog control 5939 var _closeModalControl = function ($modalDialog, $manager) { 5940 if ($modalDialog && $modalDialog.length && $modalDialog.modal) { 5941 $modalDialog.modal('hide'); 5942 } 5943 5944 // remove the modal-open class from this modal's manager 5945 if ($manager && $manager.length) { 5946 $manager.each(function () { 5947 $(this).removeClass('modal-open'); 5948 }); 5949 } 5950 5951 // if all modals are closed, remove the modal-open class from the body 5952 if ($('.modal:visible').length === 0) { 5953 $('body').removeClass('modal-open').css('padding-right', ''); 5954 } 5955 5956 // Ensure any modalBackdrops are removed if its owner is no longer visible. Note that some 5957 // backdrops do not have an owner 5958 $('.modal-backdrop').each(function () { 5959 var $modalBackdrop = $(this); 5960 var ownerId = $modalBackdrop.data('modalId'); 5961 var $owner = $('#' + ownerId); 5962 var isOwnerInDom = $owner.length > 0; 5963 5964 if (ownerId && (!isOwnerInDom || !$owner.is(':visible'))) { 5965 $modalBackdrop.remove(); 5966 } 5967 }); 5968 }; 5969 5970 // shows a non-IFrame modal dialog control 5971 var _showModalControl = function ($modalDialog, managerId, clickBackdropToClose, $hfModalVisible) { 5972 $('body').addClass('modal-open'); 5973 if (document.body.clientHeight > window.innerHeight) { 5974 $('body').css('padding-right', Rock.controls.util.getScrollbarWidth()); 5975 } 5976 5977 $modalDialog.modal({ 5978 show: true, 5979 manager: managerId, 5980 backdrop: 'static', 5981 keyboard: false, 5982 attentionAnimation: '', 5983 modalOverflow: true, 5984 replace: true 5985 }); 5986 5987 if ($('.modal-backdrop').filter(':visible').length === 0) { 5988 // 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 5989 $('<div class="modal-backdrop" data-modal-id="' + $modalDialog.prop('id') + '" />').appendTo('body'); 5990 } 5991 5992 if (clickBackdropToClose) { 5993 $(".modal-scrollable").click(function (event) { 5994 if (event.target === event.currentTarget) { 5995 $(".modal-scrollable").off("click"); 5996 _closeModalControl($modalDialog, $(managerId)); 5997 5998 if ($hfModalVisible && $hfModalVisible.length) { 5999 $hfModalVisible.val(0); 6000 } 6001 } 6002 }); 6003 } 6004 } 6005 6006 var exports = { 6007 // updates the side of the modal that the control is in. 6008 // this function works for both the IFrame modal and ModalDialog control 6009 updateSize: function (controlId) { 6010 var $control = typeof (controlId) == 'string' ? $('#' + controlId) : $(controlId); 6011 if ($control && $control.length) { 6012 var $modalBody = $control.closest('.modal-body'); 6013 if ($modalBody.is(':visible')) { 6014 $modalBody[0].style.minHeight = "0"; 6015 var scrollHeight = $modalBody.prop('scrollHeight'); 6016 if ($modalBody.outerHeight() != scrollHeight) { 6017 // if modalbody didn't already grow to fit (maybe because of a bootstrap dropdown) make modal-body big enough to fit. 6018 $modalBody[0].style.minHeight = scrollHeight + "px"; 6019 6020 // force the resizeDetector to fire 6021 if ($('#dialog').length && $('#dialog')[0].resizedAttached) { 6022 $('#dialog')[0].resizedAttached.call(); 6023 } 6024 } 6025 } 6026 } 6027 6028 },
6029 // closes the #modal-popup modal (IFrame Modal) 6030 close: function (msg) { 6031 // do a setTimeout so this fires after the postback 6032 $('#modal-popup').hide(); 6033 setTimeout(function () { 6034 $('#modal-popup iframe').attr('src', ''); 6035 $('#modal-popup').modal('hide'); 6036 6037 }, 0); 6038 6039 $('body').removeClass('modal-open').css('padding-right', ''); 6040 6041 if (msg && msg != '') { 6042 6043 if (msg == 'PAGE_UPDATED') { 6044 location.reload(true); 6045 } 6046 else { 6047 $('#rock-config-trigger-data').val(msg); 6048 $('#rock-config-trigger').trigger('click'); 6049 } 6050 } 6051 }, 6052 // closes a ModalDialog control (non-IFrame Modal) 6053 closeModalDialog: function ($modalDialog, $manager) { 6054 _closeModalControl($modalDialog, $manager); 6055 }, 6056 // shows the #modal-popup modal (IFrame Modal) 6057 show: function (sender, popupUrl, detailsId, postbackUrl) { 6058 _showModalPopup(sender, popupUrl); 6059 }, 6060 // shows a ModalDialog control (non-IFrame Modal) 6061 showModalDialog: function ($modalDialog, managerId, clickBackdropToClose, $hfModalVisible) { 6062 _showModalControl($modalDialog, managerId, clickBackdropToClose, $hfModalVisible); 6063 }, 6064 6065 // gets the IFrame element of the global the Modal Popup (for the IFrame Modal) 6066 getModalPopupIFrame: function () { 6067 var $modalPopup = $('#modal-popup'); 6068 var $modalPopupIFrame = $modalPopup.find('iframe'); 6069 6070 return $modalPopupIFrame; 6071 } 6072 }; 6073 6074 return exports; 6075 }()); 6076}(jQuery)); 6077; 6078(function ($) { 6079 'use strict'; 6080 window.Rock = window.Rock || {}; 6081 Rock.controls = Rock.controls || {}; 6082 6083 /** JS helper for the NoteEditor control */ 6084 Rock.controls.noteEditor = (function () { 6085 var exports = { 6086 /** initializes the JavasSript for the noteEditor control */ 6087 initialize: function (options) { 6088 6089 if (!options.id) { 6090 throw 'id is required'; 6091 } 6092 6093 var self = this; 6094 6095 var $control = $('#' + options.id); 6096 6097 if ($control.length == 0) { 6098 return; 6099 } 6100 6101 self.$noteEditor = $control; 6102 6103 this.initializeEventHandlers(); 6104 6105 if (options.isEditing) { 6106 var $noteContainer = self.$noteEditor.closest('.js-notecontainer'); 6107 6108 if (options.currentNoteId) { 6109 // editing an existing note 6110 var $displayedNote = $noteContainer.find("[data-note-id='" + options.currentNoteId + "']"); 6111 6112 // move note editor and display in place of the readonly version of note 6113 self.$noteEditor.detach(); 6114 $displayedNote.parent('.js-note').prepend(self.$noteEditor); 6115 self.$noteEditor.fadeIn(); 6116 6117 $displayedNote.hide(); 6118 } 6119 else if (options.parentNoteId) { 6120 // new reply to a note 6121 var $replyToNote = $noteContainer.find("[data-note-id='" + options.parentNoteId + "']"); 6122 6123 // move note editor as a child note of the note we are replying to 6124 self.$noteEditor.detach(); 6125 $replyToNote.append(self.$noteEditor) 6126 self.$noteEditor.slideDown().find('textarea').trigger("focus"); 6127 } 6128 else { 6129 // new note, so just show it 6130 self.$noteEditor.fadeIn(); 6131 } 6132 } 6133 }, 6134 6135 /** */ 6136 initializeEventHandlers: function () { 6137 // Initialize NoteEditor and NoteContainer events 6138 var self = this;
6139 var $noteContainer = self.$noteEditor.closest('.js-notecontainer'); 6140 6141 $('.js-addnote,.js-editnote,.js-replynote', $noteContainer).on('click', function (e) { 6142 var addNote = $(this).hasClass('js-addnote'); 6143 var editNote = $(this).hasClass('js-editnote'); 6144 var replyNote = $(this).hasClass('js-replynote'); 6145 var cancelNote = $(this).hasClass('js-editnote-cancel'); 6146 var deleteNote = $(this).hasClass('js-removenote'); 6147 6148 var sortDirection = $noteContainer.data('sortdirection'); 6149 var $noteEditor = $noteContainer.find('.js-note-editor'); 6150 var $currentNote = $(false); 6151 $noteEditor.detach(); 6152 6153 // clear out any previously entered stuff 6154 $noteEditor.find('.js-parentnoteid').val(''); 6155 $noteEditor.find('textarea').val(''); 6156 $noteEditor.find('input:checkbox').prop('checked', false); 6157 $noteEditor.find('.js-notesecurity').hide(); 6158 6159 var $noteprivateInput = $noteEditor.find('.js-noteprivate'); 6160 $noteprivateInput.parent().show(); 6161 6162 if (addNote) { 6163 e.preventDefault(); 6164 e.stopImmediatePropagation(); 6165 var postbackJs = $noteContainer.find(".js-add-postback").attr('href'); 6166 window.location = postbackJs; 6167 return; 6168 } 6169 else { 6170 $currentNote = $(this).closest('.js-noteviewitem'); 6171 var currentNoteId = $currentNote.data('note-id'); 6172 6173 if (replyNote) { 6174 $noteContainer.find('.js-currentnoteid').val(currentNoteId); 6175 $noteEditor.find('.js-parentnoteid').val(currentNoteId); 6176 6177 e.preventDefault(); 6178 e.stopImmediatePropagation(); 6179 var postbackJs = $noteContainer.find(".js-reply-to-postback").attr('href'); 6180 window.location = postbackJs; 6181 return; 6182 } 6183 else if (editNote) { 6184 $noteContainer.find('.js-currentnoteid').val(currentNoteId); 6185 6186 e.preventDefault(); 6187 e.stopImmediatePropagation(); 6188 var postbackJs = $noteContainer.find(".js-edit-postback").attr('href'); 6189 window.location = postbackJs; 6190 return; 6191 } 6192 } 6193 }); 6194 6195 $('.js-notesecurity', $noteContainer).on('click', function (e) { 6196 var $securityBtn = $(this); 6197 var entityTypeId = $securityBtn.data('entitytype-id'); 6198 var title = $securityBtn.data('title'); 6199 var currentNoteId = $securityBtn.data('entity-id'); 6200 var securityUrl = Rock.settings.get('baseUrl') + "Secure/" + entityTypeId + "/" + currentNoteId + "?t=" + title + "&pb=&sb=Done"; 6201 Rock.controls.modal.show($securityBtn, securityUrl); 6202 }); 6203 6204 $('.js-editnote-cancel', $noteContainer).on('click', function (e) { 6205 var $noteEditor = $noteContainer.find('.js-note-editor'); 6206 $noteEditor.slideUp(); 6207 6208 // show any notedetails that might have been hidden when doing the editing 6209 $noteEditor.parent().find('.js-noteviewitem').slideDown(); 6210 }); 6211 6212 $('.js-removenote', $noteContainer).on('click', function (e) { 6213 var $currentNote = $(this).closest('.js-noteviewitem'); 6214 var currentNoteId = $currentNote.attr('data-note-id'); 6215 $noteContainer.find('.js-currentnoteid').val(currentNoteId); 6216 6217 e.preventDefault(); 6218 e.stopImmediatePropagation(); 6219 var postbackJs = $noteContainer.find(".js-delete-postback").attr('href'); 6220 return Rock.dialogs.confirm('Are you sure you want to delete this note?', function (result) { 6221 if (result) { 6222 window.location = postbackJs; 6223 } 6224 }); 6225 }); 6226 6227 $('.js-expandreply', $noteContainer).on('click', function (e) { 6228 var $currentNote = $(this).closest('.js-note'); 6229 var $childNotesContainer = $currentNote.find('.js-childnotes').first(); 6230 $childNotesContainer.slideToggle(function (x) { 6231 6232 // get a list of noteIds that have their child items visible, so that we can maintain that expansion after a postback 6233 var expandedNoteIds = $(this).closest('.js-notec
6233ontainer').find('.js-noteviewitem:visible').map(function () { 6234 var $noteItem = $(this).closest('.js-note'); 6235 var $childNotesExpanded = $noteItem.find('.js-childnotes:first').is(':visible'); 6236 if ($childNotesExpanded) { 6237 return $(this).attr('data-note-id'); 6238 } 6239 else { 6240 return null; 6241 } 6242 }).get().join(); 6243 6244 var $expandedNoteIdsHiddenField = $noteContainer.find('.js-expandednoteids'); 6245 $expandedNoteIdsHiddenField.val(expandedNoteIds); 6246 }); 6247 }); 6248 } 6249 } 6250 6251 return exports 6252 }()); 6253}(jQuery)); 6254; 6255(function ($) { 6256 'use strict'; 6257 window.Rock = window.Rock || {}; 6258 Rock.controls = Rock.controls || {}; 6259 6260 Rock.controls.numberBox = (function () { 6261 var exports = { 6262 clientValidate: function (validator, args) { 6263 var validationControl = $(validator); 6264 var numberType = validationControl.attr('type'); 6265 6266 // Only validate numeric types - currency and other formats require server validation. 6267 if (!(numberType === 'double' || numberType === 'integer')) { 6268 return; 6269 } 6270 6271 var $numberBox = $(validator).closest('.js-number-box'); 6272 var isValid = true; 6273 var validationMessage = ''; 6274 6275 var label = $numberBox.find('label').text(); 6276 if (!label) { 6277 label = "Value"; 6278 } 6279 6280 var value = args.Value; 6281 6282 // Check for a valid number - allow a signed or unsigned decimal with or without digit separators. 6283 var floatRegex = /(?=.*?\d)^[-,+]?(([1-9]\d{0,2}(,\d{3})*)|\d+)?(\.\d{1,9})?$/s; 6284 validationMessage = label + " must be a valid number."; 6285 isValid = floatRegex.test(value); 6286 6287 // Get the numeric value. 6288 value = parseFloat(value); 6289 6290 // Check for integer. 6291 if (isValid) { 6292 if (numberType === "integer") { 6293 validationMessage = label + " must be an integer value."; 6294 isValid = Number.isInteger(value); 6295 } 6296 } 6297 6298 // Check range. 6299 if (isValid) { 6300 var max = parseFloat(validationControl.attr("max")); 6301 var min = parseFloat(validationControl.attr("min")); 6302 var checkMaxValue = !Number.isNaN( max ); 6303 var checkMinValue = !Number.isNaN( min ); 6304 6305 if ( checkMinValue && checkMaxValue) { 6306 validationMessage = label + ' must have a value between ' + min + ' and ' + max + '.'; 6307 isValid = (value >= min && value <= max); 6308 } 6309 else if (checkMinValue) { 6310 validationMessage = label + ' must have a value of ' + min + ' or more.'; 6311 isValid = (value >= min); 6312 } 6313 else if (checkMaxValue) { 6314 validationMessage = label + ' must have a value of ' + max + ' or less.'; 6315 isValid = (value <= max); 6316 } 6317 } 6318 6319 args.IsValid = isValid; 6320 6321 // Set the visual feedback. 6322 if (isValid) { 6323 $numberBox.removeClass('has-error'); 6324 $numberBox.attr('title', ''); 6325 } 6326 else { 6327 $numberBox.addClass('has-error'); 6328 $numberBox.attr('title', validationMessage); 6329 6330 validator.errormessage = validationMessage;
6331 validator.isvalid = false; 6332 } 6333 } 6334 }; 6335 6336 return exports; 6337 }()); 6338}(jQuery)); 6339; 6340(function ($) { 6341 'use strict'; 6342 window.Rock = window.Rock || {}; 6343 Rock.controls = Rock.controls || {}; 6344 6345 Rock.controls.numberRangeEditor = (function () { 6346 var exports = { 6347 clientValidate: function (validator, args) { 6348 var $numberRangeEditor = $(validator).closest('.js-numberrangeeditor'); 6349 var lowerValue = $numberRangeEditor.find('input.js-number-range-lower ').val(); 6350 var upperValue = $numberRangeEditor.find('input.js-number-range-upper').val(); 6351 var required = $numberRangeEditor.attr('data-required') == 'true'; 6352 var itemLabelText = $numberRangeEditor.attr('data-itemlabel'); 6353 6354 var isValid = true; 6355 6356 if (required) { 6357 // if required, then make sure that the number range has a lower and/or upper value (can't both be blank) 6358 if (lowerValue.length === 0 && upperValue.length === 0) { 6359 isValid = false; 6360 validator.errormessage = itemLabelText + ' is required'; 6361 } 6362 } 6363 6364 var control = $numberRangeEditor; 6365 var labelForSelector = "label[for='" + $numberRangeEditor.prop('id') + "']"; 6366 var $labelFor = $(labelForSelector) 6367 if (isValid) { 6368 control.removeClass('has-error'); 6369 $labelFor.parent().removeClass('has-error'); 6370 } 6371 else { 6372 control.addClass('has-error'); 6373 $labelFor.parent().addClass('has-error'); 6374 } 6375 6376 args.IsValid = isValid; 6377 } 6378 }; 6379 6380 return exports; 6381 }()); 6382}(jQuery)); 6383; 6384(function ($) { 6385 'use strict'; 6386 window.Rock = window.Rock || {}; 6387 Rock.controls = Rock.controls || {}; 6388 6389 Rock.controls.numberUpDown = (function () { 6390 6391 var exports = { 6392 adjust: function (btn, adjustment, postbackScript) { 6393 6394 var $parent = $(btn).closest('div.numberincrement'); 6395 var $min = $parent.find('.js-number-up-down-min').first(); 6396 var $max = $parent.find('.js-number-up-down-max').first(); 6397 var $value = $parent.find('.js-number-up-down-value').first(); 6398 var $lbl = $parent.find('.js-number-up-down-lbl').first(); 6399 var $upBtn = $parent.find('.js-number-up').first(); 6400 var $downBtn = $parent.find('.js-number-down').first(); 6401 6402 // Get the min, max, and new value 6403 var minValue = parseInt($min.val(), 10); 6404 var maxValue = parseInt($max.val(), 10); 6405 var numValue = parseInt($value.val(), 10) + adjustment; 6406 6407 // If new value is valid, set the hf and lbl 6408 if (numValue >= minValue && numValue <= maxValue) { 6409 $value.val(numValue); 6410 $lbl.html(numValue); 6411 } 6412 6413 // enable/disable the 'up' button 6414 if (numValue >= maxValue) { 6415 $upBtn.addClass('disabled'); 6416 } else { 6417 $upBtn.removeClass('disabled'); 6418 } 6419 6420 // enable/disable the 'down' button 6421 if (numValue <= minValue) { 6422 $downBtn.addClass('disabled'); 6423 } else { 6424 $downBtn.removeClass('disabled'); 6425 } 6426 6427 if (postbackScript && postbackScript != "" ) { 6428 window.location = "javascript:" + postbackScript; 6429 } 6430 6431 } 6432 }; 6433 6434 return exports; 6435 }()); 6436}(jQuery));; 6437(function ($) 6438{ 6439 'use strict'; 6440 window.Rock = window.Rock || {}; 6441 Rock.controls = Rock.controls || {}; 6442 6443 Rock.controls.numberUpDownGroup = (function () 6444 { 6445 var exports = { 6446 clientValidate: function (validator, args) 6447 { 6448 var $numberUpDownGroup = $(validator).closest('.js-number-up-down-group'); 6449 var isValid = true; 6450 6451 if ($numberUpDownGroup.hasClass('required') === true) { 6452 isValid = false; 6453 6454 $numberUpDownGroup.find('.js-number-up-down-lbl').each(function (i) { 6455 if (parseInt(this.innerText, 10) > 0) { 6456 isValid = true; 6457 } 6458 }); 6459 } 6460 6461 if (isValid === false) { 6462 validator.errormessage = $numberUpDownGroup.find('label').text() + " is required"; 6463 } 6464
6465 var control = $numberUpDownGroup; 6466 if (isValid) 6467 { 6468 control.removeClass('has-error'); 6469 } 6470 else 6471 { 6472 control.addClass('has-error'); 6473 } 6474 6475 args.IsValid = isValid; 6476 } 6477 }; 6478 6479 return exports; 6480 }()); 6481}(jQuery)); 6482; 6483(function ($) { 6484 'use strict'; 6485 window.Rock = window.Rock || {}; 6486 Rock.controls = Rock.controls || {}; 6487 6488 Rock.controls.pbx = (function () { 6489 6490 var exports = { 6491 // implements the click to call feature 6492 originate: function (sourcePersonGuid, destinationNumber, callerId, destinationPersonName, destinationNumberFormatted) { 6493 var originateUrl = Rock.settings.get('baseUrl') + 'api/Pbx/Originate?sourcePersonGuid=' + sourcePersonGuid + '&destinationPhone=' + destinationNumber + '&callerId=' + callerId; 6494 6495 console.log('From Rock.controls.originateCall: ' + originateUrl); 6496 $.get(originateUrl, function (r) { 6497 if (!r.Success) { 6498 Rock.dialogs.alert("An error occurred while attempting to place call. " + r.Message); 6499 } 6500 }); 6501 } 6502 }; 6503 6504 return exports; 6505 }()); 6506}(jQuery));; 6507(function ($) { 6508 'use strict'; 6509 window.Rock = window.Rock || {}; 6510 Rock.controls = Rock.controls || {}; 6511 6512 Rock.controls.personPicker = (function () { 6513 var PersonPicker = function (options) { 6514 this.controlId = options.controlId; 6515 this.restUrl = options.restUrl; 6516 this.restDetailUrl = options.restDetailUrl; 6517 this.defaultText = options.defaultText || ''; 6518 this.iScroll = null; 6519 this.$pickerControl = $('#' + this.controlId); 6520 this.$pickerScrollContainer = this.$pickerControl.find('.js-personpicker-scroll-container'); 6521 }; 6522 6523 PersonPicker.prototype.initializeEventHandlers = function () { 6524 var controlId = this.controlId, 6525 restUrl = this.restUrl, 6526 restDetailUrl = this.restDetailUrl || (Rock.settings.get('baseUrl') + 'api/People/GetSearchDetails'), 6527 defaultText = this.defaultText; 6528 6529 var $pickerControl = this.$pickerControl; 6530 var $searchFields = $pickerControl.find('.js-personpicker-search-field input'); 6531 var $searchFieldName = $pickerControl.find('.js-personpicker-search-name input'); 6532 var $searchFieldAddress = $pickerControl.find('.js-personpicker-search-address input'); 6533 var $searchFieldPhone = $pickerControl.find('.js-personpicker-search-phone input'); 6534 var $searchFieldEmail = $pickerControl.find('.js-personpicker-search-email input'); 6535 var $searchResults = $pickerControl.find('.js-personpicker-searchresults'); 6536 var $pickerToggle = $pickerControl.find('.js-personpicker-toggle'); 6537 var $pickerMenu = $pickerControl.find('.js-personpicker-menu'); 6538 var $pickerSelect = $pickerControl.find('.js-personpicker-select'); 6539 var $pickerSelectNone = $pickerControl.find('.js-picker-select-none'); 6540 var $pickerPersonId = $pickerControl.find('.js-person-id'); 6541 var $pickerPersonName = $pickerControl.find('.js-person-name'); 6542 var $pickerCancel = $pickerControl.find('.js-personpicker-cancel'); 6543 var $pickerToggleAdditionalSearchFields = $pickerControl.find('.js-toggle-additional-search-fields'); 6544 var $pickerAdditionalSearchFields = $pickerControl.find('.js-personpicker-additional-search-fields'); 6545 var $pickerExpandSearchFields = $pickerControl.find('.js-expand-search-fields'); 6546 6547 var includeBusinesses = $pickerControl.find('.js-include-businesses').val() == '1' ? 'true' : 'false'; 6548 var includeDeceased = $pickerControl.find('.js-include-deceased').val() == '1' ? 'true' : 'false'; 6549 var includeDetails = 'true'; 6550 6551 var promise = null; 6552 var lastSelectedPersonId = null; 6553 6554 var autoCompletes = $searchFields.autocomplete({ 6555 source: function (request, response) { 6556 6557 var search = { 6558 name: $searchFieldName.val(), 6559 address: $searchFieldAddress.val(), 6560 phone: $searchFieldPhone.val(), 6561 email: $searchFieldEmail.val() 6562 }; 6563 6564 // make sure that at least one of the search f
6564ields has 3 chars in it 6565 if ((search.name.length < 3) && (search.address.length < 3) && (search.phone.length < 3) && (search.email.length < 3)) { 6566 return; 6567 } 6568 6569 // abort any searches that haven't returned yet, so that we don't get a pile of results in random order 6570 if (promise && promise.state() === 'pending') { 6571 promise.abort(); 6572 } 6573 6574 var searchParams = []; 6575 if (search.name) { 6576 searchParams.push("name=" + encodeURIComponent(search.name)); 6577 } 6578 6579 // also search additional search fields if they are visible 6580 if (search.address && $searchFieldAddress.is(':visible')) { 6581 searchParams.push("address=" + encodeURIComponent(search.address)); 6582 } 6583 6584 if (search.phone && $searchFieldPhone.is(':visible')) { 6585 searchParams.push("phone=" + encodeURIComponent(search.phone)); 6586 } 6587 6588 if (search.email && $searchFieldEmail.is(':visible')) { 6589 searchParams.push("email=" + encodeURIComponent(search.email)); 6590 } 6591 6592 var searchQuery = "?" + searchParams.join("&"); 6593 6594 // set the timeout to 20 seconds, just in case it takes a long time to search 6595 promise = $.ajax({ 6596 url: restUrl 6597 + searchQuery 6598 + "&includeDetails=" + includeDetails 6599 + "&includeBusinesses=" + includeBusinesses 6600 + "&includeDeceased=" + includeDeceased, 6601 timeout: 20000, 6602 dataType: 'json' 6603 }); 6604 6605 // Display a wait indicator to show that the search is now running. 6606 if ($('.js-searching-notification').length == 0) { 6607 $searchResults.prepend('<i class="ti ti-refresh ti-spin margin-l-md js-searching-notification" style="display: none; opacity: .4;"></i>'); 6608 } 6609 $('.js-searching-notification').fadeIn(800); 6610 6611 promise.done(function (data) { 6612 $searchResults.html(''); 6613 response($.map(data, function (item) { 6614 return item; 6615 })); 6616 6617 exports.personPickers[controlId].updateScrollbar(); 6618 }); 6619 6620 promise.fail(function (xhr, status, error) { 6621 console.log(status + ' [' + error + ']: ' + xhr.responseText); 6622 var errorCode = xhr.status; 6623 if (errorCode == 401) { 6624 $searchResults.html("<li class='text-danger'>Sorry, you're not authorized to search.</li>"); 6625 } 6626 6627 $('.js-searching-notification').remove(); 6628 }); 6629 }, 6630 // Set minLength to 0, but check that at least one field has 3 chars before fetching from REST. 6631 // To minimize load on the server, don't trigger the search until a reasonable delay after the last keypress. 6632 minLength: 0, 6633 delay: 750, 6634 html: true, 6635 appendTo: $searchResults, 6636 pickerControlId: controlId, 6637 messages: { 6638 noResults: function () { }, 6639 results: function () { } 6640 } 6641 }); 6642 6643 var autoCompleteCustomRenderItem = function ($ul, item) { 6644 if (this.options.html) { 6645 // override jQueryUI autocomplete's _renderItem so that we can do HTML for the ListItems 6646 // derived from http://github.com/scottgonzalez/jquery-ui-extensions 6647 6648 var inactiveWarning = ""; 6649 6650 if (!item.IsActive && item.RecordStatus) { 6651 inactiveWarning = " <small>(" + item.RecordStatus + ")</small>"; 6652 } 6653 if (item.IsDeceased) { 6654 inactiveWarning = " <small class=\"text-danger\">(Deceased)</small>"; 6655 } 6656 6657 var quickSummaryInfo = ""; 6658 if (item.FormattedAge || item.SpouseNickName) { 6659 quickSummaryInfo = " <small class='rollover-item text-muted'>"; 6660 if (item.FormattedAge) { 6661 quickSummaryInfo += "Age: " + item.FormattedAge; 6662 } 6663 6664 if (item.SpouseNickName) { 6665 if (item.FormattedAge) { 6666 quickSummaryInfo += "; "; 6667 } 6668 6669 quickSummaryInfo += "Spouse: " + item.SpouseNickName; 6670 } 6671 6672 quickSummaryInfo += "</small>"; 6673 } 6674
6675 var $div = $('<div/>').attr('class', 'radio'), 6676 6677 $label = $('<label/>') 6678 .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>') 6679 .prependTo($div), 6680 6681 $radio = $('<input type="radio" name="person-id" />') 6682 .attr('id', item.Id) 6683 .attr('value', item.Id) 6684 .prependTo($label), 6685 6686 $li = $('<li/>') 6687 .addClass('picker-select-item js-picker-select-item') 6688 .attr('data-person-id', item.Id) 6689 .attr('data-person-name', item.Name) 6690 .html($div), 6691 6692 $resultSection = $(this.options.appendTo); 6693 6694 var $itemDetailsDiv = $('<div/>') 6695 .addClass('picker-select-item-details js-picker-select-item-details clearfix'); 6696 6697 if (item.SearchDetailsHtml) {
6698 $itemDetailsDiv.attr('data-has-details', true).html(item.SearchDetailsHtml); 6699 } 6700 else { 6701 $itemDetailsDiv.attr('data-has-details', false); 6702 } 6703 6704 if (includeDetails === 'false') { 6705 $itemDetailsDiv.hide(); 6706 } 6707 6708 $itemDetailsDiv.appendTo($li); 6709 6710 if (!item.IsActive) { 6711 $li.addClass('is-inactive'); 6712 } 6713 6714 return $resultSection.append($li); 6715 } 6716 else { 6717 return $('<li></li>') 6718 .data('item.autocomplete', item) 6719 .append($('<a></a>').text(item.label)) 6720 .appendTo($ul); 6721 } 6722 } 6723 6724 // each search field has its own autocomplete object, so we'll need override with our custom _renderItem to each 6725 $.each(autoCompletes, function (a, b, c) { 6726 var autoComplete = $(autoCompletes[a]).data('ui-autocomplete'); 6727 6728 // Debugging: override close to prevent it canceling when loosing focus when debugging 6729 // autoComplete.close = function () { }; 6730 6731 autoComplete._renderItem = autoCompleteCustomRenderItem; 6732 }); 6733 6734 $pickerToggle.on('click', function (e) { 6735 e.preventDefault(); 6736 $(this).toggleClass("active"); 6737 $pickerMenu.toggle(0, function () { 6738 exports.personPickers[controlId].updateScrollbar(); 6739 $searchFieldName.trigger('focus'); 6740 }); 6741 }); 6742 6743 $pickerControl.on('click', '.js-picker-select-item', function (e) { 6744 if (e.type == 'click' && $(e.target).is(':input') == false) { 6745 // only process the click event if it has bubbled up to the input tag 6746 return; 6747 } 6748 6749 e.stopPropagation(); 6750 6751 var $selectedItem = $(this).closest('.js-picker-select-item'); 6752 var $itemDetails = $selectedItem.find('.js-picker-select-item-details'); 6753 6754 var selectedPersonId = $selectedItem.attr('data-person-id'); 6755 6756 if ($itemDetails.is(':visible')) { 6757 6758 if (selectedPersonId == lastSelectedPersonId && e.type == 'click') { 6759 // 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 6760 var selectedText = $selectedItem.attr('data-person-name'); 6761 6762 setSelectedPerson(selectedPersonId, selectedText); 6763 6764 $pickerSelect.trigger('onclick'); 6765 // Fire the postBack for the Select button. 6766 var postBackUrl = $pickerSelect.prop('href'); 6767 if (postBackUrl) { 6768 window.location = postBackUrl; 6769 } 6770 } else { 6771 6772 // if it is already visible but isn't the same one twice, just leave it open 6773 } 6774 } 6775 6776 if (includeDetails === 'false') { 6777 // hide other open details 6778 $('.js-picker-select-item-details', $pickerControl).filter(':visible').each(function () { 6779 var $el = $(this), 6780 currentPersonId = $el.closest('.js-picker-select-item').attr('data-person-id'); 6781 6782 if (currentPersonId != selectedPersonId) { 6783 $el.slideUp(); 6784 exports.personPickers[controlId].updateScrollbar(); 6785 } 6786 }); 6787 } 6788 6789 lastSelectedPersonId = selectedPersonId; 6790
6791 if ($itemDetails.attr('data-has-details') == 'false') { 6792 // add a spinner in case we have to wait on the server for a little bit 6793 var $spinner = $selectedItem.find('.loading-notification'); 6794 $spinner.fadeIn(800); 6795 6796 // fetch the search details from the server 6797 $.get(restDetailUrl + '?Id=' + selectedPersonId, function (responseText, textStatus, jqXHR) { 6798 $itemDetails.attr('data-has-details', true); 6799 6800 // hide then set the HTML so that we can get the slideDown effect 6801 $itemDetails.stop().hide().html(responseText); 6802 showItemDetails($itemDetails); 6803 6804 $spinner.stop().fadeOut(200); 6805 }); 6806 } else { 6807 showItemDetails($selectedItem.find('.picker-select-item-details:hidden')); 6808 } 6809 }); 6810 6811 var showItemDetails = function ($itemDetails) { 6812 if ($itemDetails.length) { 6813 $itemDetails.slideDown(function () { 6814 exports.personPickers[controlId].updateScrollbar(); 6815 }); 6816 } 6817 } 6818 6819 $pickerControl.on('mouseenter', 6820 function () { 6821 // only show the X if there is something picked 6822 if (($pickerPersonId.val() || '0') !== '0') { 6823 $pickerSelectNone.addClass('show-hover'); 6824 } 6825 }); 6826 6827 $pickerCancel.on('click', function () { 6828 6829 clearSearchFields(); 6830 $pickerMenu.slideUp(function () { 6831 exports.personPickers[controlId].updateScrollbar(); 6832 }); 6833 }); 6834 6835 $pickerSelectNone.on('click', function (e) { 6836 // prevent the click from bubbling up to the pickerControl click event 6837 e.preventDefault(); 6838 e.stopPropagation(); 6839 6840 var selectedValue = '0', 6841 selectedText = defaultText; 6842 6843 $pickerPersonId.val(selectedValue); 6844 $pickerPersonName.val(selectedText); 6845 // run onclick event from the button 6846 $(this).trigger('onclick'); 6847 }); 6848 6849 // disable the enter key : this will prevent the enter key from clearing the loaded search query. 6850 $pickerControl.on('keypress', function (e) { 6851 if (e.which == 13) { 6852 return false; 6853 } 6854 }); 6855 6856 var setSelectedPerson = function (selectedValue, selectedText) { 6857 var selectedPersonLabel = $pickerControl.find('.js-personpicker-selectedperson-label'); 6858 6859 $pickerPersonId.val(selectedValue); 6860 $pickerPersonName.val(selectedText); 6861 6862 selectedPersonLabel.val(selectedValue); 6863 selectedPersonLabel.text(selectedText); 6864 6865 $pickerMenu.slideUp(); 6866 } 6867 6868 var clearSearchFields = function () { 6869 $searchFieldName.val(''); 6870 $searchFieldAddress.val(''); 6871 $searchFieldPhone.val(''); 6872 $searchFieldEmail.val(''); 6873 } 6874 6875 $pickerSelect.on('click', function () { 6876 var $radInput = $pickerControl.find('input:checked'), 6877 selectedValue = $radInput.val(), 6878 selectedText = $radInput.closest('.js-picker-select-item').attr('data-person-name'); 6879 6880 setSelectedPerson(selectedValue, selectedText); 6881 clearSearchFields(); 6882 }); 6883 6884 var toggleSearchFields = function () { 6885 var expanded = $pickerExpandSearchFields.val(); 6886 if (expanded == 1) { 6887 $pickerAdditionalSearchFields.slideDown(); 6888 } 6889 else { 6890 $pickerAdditionalSearchFields.slideUp(); 6891 } 6892 6893 $pickerToggleAdditionalSearchFields.toggleClass('active', expanded == 1); 6894 }; 6895 6896 toggleSearchFields(); 6897 6898 $pickerToggleAdditionalSearchFields.on('click', function () { 6899 var expanded = $pickerExpandSearchFields.val(); 6900 if (expanded == 1) { 6901 expanded = 0; 6902 } 6903 else { 6904 expanded = 1; 6905 } 6906 6907 $pickerExpandSearchFields.val(expanded); 6908 6909 toggleSearchFields(); 6910 }); 6911 6912 $('.js-select-self', $pickerControl).on('click', function () { 6913 var selectedValue = $('.js-self-person-id', $pickerControl).val(), 6914 selectedText = $('.js-self-person-name', $pickerControl).val(); 6915 6916 setSelectedPerson(selectedValue, selectedText); 6917 6918 // fire the postBack of the btnSelect if there is one 6919 $pickerSelect.trigger('onclick'); 6920 var postBackUrl = $pickerSelect.prop('href'); 6921 if (postBackUrl) { 6922 window.location = postBackUrl; 6923 } 6924 }); 6925 }; 6926 6927 PersonPicker.prototype.updateScrollbar = function () { 6928 var self = this; 6929 6930 // first, update this control's scrollbar, then the modal'
6930s 6931 6932 if (self.$pickerScrollContainer.is(':visible')) { 6933 if (self.iScroll) { 6934 self.iScroll.refresh(); 6935 } 6936 } 6937 6938 // update the outer modal scrollbar 6939 Rock.dialogs.updateModalScrollBar(this.controlId); 6940 } 6941 6942 PersonPicker.prototype.initialize = function () { 6943 6944 this.iScroll = new IScroll($('.viewport', this.$pickerControl)[0], { 6945 mouseWheel: true, 6946 indicators: { 6947 el: $('.track', this.$pickerScrollContainer)[0], 6948 interactive: true, 6949 resize: false, 6950 listenY: true, 6951 listenX: false, 6952 }, 6953 click: false, 6954 preventDefaultException: { tagName: /.*/ } 6955 }); 6956 6957 this.initializeEventHandlers(); 6958 }; 6959 6960 var exports = { 6961 personPickers: {}, 6962 findControl: function (controlId) { 6963 return exports.personPickers[controlId]; 6964 }, 6965 initialize: function (options) { 6966 if (!options.controlId) throw '`controlId` is required.'; 6967 if (!options.restUrl) throw '`restUrl` is required.'; 6968 6969 var personPicker = new PersonPicker(options); 6970 exports.personPickers[options.controlId] = personPicker; 6971 personPicker.initialize(); 6972 } 6973 }; 6974 return exports; 6975 }()); 6976}(jQuery)); 6977; 6978(function($) { 6979 'use strict'; 6980 window.Rock = window.Rock || {}; 6981 Rock.controls = Rock.controls || {}; 6982 6983 Rock.controls.priorityNav = (function() { 6984 var RESIZE_DURATION = 500; 6985 var TAB_KEYCODE = 9; 6986 var ClassName = { 6987 PRIORITY: 'priority', 6988 HIDE: 'sr-only', 6989 RESIZING: 'resizing' 6990 }; 6991 var Selector = { 6992 NAV_ELEMENTS: "li:not('.overflow-nav')", 6993 FIRST_ELEMENT: 'li:first', 6994 PRIORITY_ELEMENT: '.priority' 6995 }; 6996 var MenuLabelDefault = 'More'; 6997 var MenuLabelAllHiddenDefault = 'Menu'; 6998 var MenuLabelCarat = '<i class="ml-1 ti ti-chevron-down" />' 6999 var MenuTemplate = function(MenuLabel) { 7000 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>'; 7001 }; 7002 var PriorityNav = function (options) { 7003 var element = $('.nav-tabs'); 7004 7005 if (options && options.controlId) { 7006 element = $('#' + options.controlId); 7007 } else { 7008 options = ''; 7009 } 7010 7011 this._element = element; 7012 this._config = options; 7013 7014 if ($(element).is('ul')) { 7015 this._$menu = $(element); 7016 } else { 7017 this._$menu = $(element) 7018 .find('ul') 7019 .first(); 7020 } 7021 this._initMenu(); 7022 this._$allNavElements = this._$menu.find(Selector.NAV_ELEMENTS); 7023 this._bindUIActions(); 7024 this._setupMenu(); 7025 }; 7026 7027 PriorityNav.prototype = { 7028 constructor: PriorityNav, 7029 initialize: function() {}, 7030 _initMenu: function() { 7031 var MenuLabel = this._config.MenuLabel 7032 7033 if (typeof MenuLabel === 'undefined') { 7034 MenuLabel = MenuLabelDefault 7035 } 7036 7037 // add menu template 7038 this._$menu.append(MenuTemplate(MenuLabel)); 7039 }, 7040 7041 _setupMenu: function() { 7042 var $allNavElements = this._$allNavElements; 7043 // Checking position of the menu 7044 var menuPosition = this._$menu.position(); 7045 // Get position of right 7046 var menuRight = menuPosition.left + this._$menu.outerWidth(); 7047 // Checking top position of first item (sometimes changes) 7048 var firstPos = this._$menu.find(Selector.FIRST_ELEMENT).position(); 7049 7050 // Empty collection in which to put menu items to move 7051 var $wrappedElements = $(); 7052 7053 // Used to snag the previous menu item in addition to ones that have wrapped 7054 var first = true; 7055 7056 // Loop through all the nav items... 7057 this._$allNavElements.each(function(i) { 7058 var $elm = $(this); 7059 7060 // ...in which to find wrapped elements 7061 var pos = $elm.position(); 7062 // Get position of right 7063 var right = pos.left + $elm.outerWidth(); 7064 7065 if (pos.top !== firstPos.top || right > menuRight) {
7066 // If element is wrapped, add it to set 7067 $wrappedElements = $wrappedElements.add($elm); 7068 7069 // Add the previous one too, if first 7070 if (first) { 7071 $wrappedElements = $wrappedElements.add($allNavElements.eq(i - 1)); 7072 first = false; 7073 } 7074 } 7075 }); 7076 7077 if ($wrappedElements.length) { 7078 // Clone set before altering 7079 var newSet = $wrappedElements.clone(); 7080 7081 // Hide ones that we're moving 7082 $wrappedElements.addClass(ClassName.HIDE); 7083 $wrappedElements.find('a').attr('tabindex', -1); 7084 7085 // Add wrapped elements to dropdown 7086 this._$menu.find('.overflow-nav-list').append(newSet); 7087 7088 // Show new menu 7089 this._$menu.find('.overflow-nav').removeClass('d-none'); 7090 7091 // Check if menu doesn't overflow after process 7092 if (this._$menu.find('.overflow-nav').position().top !== firstPos.top) { 7093 var $item = $(this._element) 7094 .find('.' + ClassName.HIDE) 7095 .first() 7096 .prev(); 7097 var $itemDuplicate = $item.clone(); 7098 7099 $item.addClass(ClassName.HIDE); 7100 $item.find('a').attr('tabindex', -1); 7101 7102 this._$menu.find('.overflow-nav-list').prepend($itemDuplicate); 7103 } 7104 7105 if ($allNavElements.length == $wrappedElements.length) { 7106 this._$menu.find('.overflow-nav-link').html(MenuLabelAllHiddenDefault + ' ' + MenuLabelCarat); 7107 this._$menu.find('.overflow-nav-list').removeClass('dropdown-menu-right'); 7108 } else { 7109 this._$menu.find('.overflow-nav-link').html(MenuLabelDefault + ' ' + MenuLabelCarat); 7110 this._$menu.find('.overflow-nav-list').addClass('dropdown-menu-right'); 7111 } 7112 } 7113 7114 // hide menu from AT 7115 this._$menu.find('.overflow-nav').attr('aria-hidden', true); 7116 }, 7117 7118 _tearDown: function() { 7119 this._$menu.find('.overflow-nav-list').empty(); 7120 this._$menu.find('.overflow-nav').addClass('d-none'); 7121 this._$allNavElements.removeClass(ClassName.HIDE); 7122 this._$allNavElements.find('a').attr('tabindex', 0); 7123 }, 7124 7125 _bindUIActions: function() { 7126 var self = this; 7127 $(window).on('resize', function() { 7128 self._$menu.addClass(ClassName.RESIZING); 7129 7130 setTimeout( function() { 7131 self._tearDown(); 7132 self._setupMenu(); 7133 self._$menu.removeClass(ClassName.RESIZING); 7134 }, RESIZE_DURATION); 7135 }); 7136 7137 this._$menu.find('.overflow-nav .dropdown-toggle').on('keyup', function(e) { 7138 if (e.which === TAB_KEYCODE) { 7139 $(e.target).dropdown('toggle'); 7140 } 7141 }); 7142 } 7143 } 7144 7145 var exports = { 7146 defaults: { 7147 controlId: null, 7148 name: 'prioritynav' 7149 }, 7150 controls: {}, 7151 initialize: function (options) { 7152 var settings = $.extend({}, exports.defaults, options); 7153 7154 if (!settings.controlId) throw 'controlId is required'; 7155 7156 var priorityNav = new PriorityNav(settings); 7157 7158 // Delay initialization until after the DOM is ready 7159 $(function () { 7160 priorityNav.initialize(); 7161 }); 7162 } 7163 }; 7164 7165 return exports; 7166 })(); 7167})(jQuery); 7168; 7169(function ($) { 7170 'use strict'; 7171 window.Rock = window.Rock || {}; 7172 Rock.controls = Rock.controls || {}; 7173 7174 Rock.controls.rangeSlider = (function () { 7175 var exports = { 7176 initialize: function (options) { 7177 if (!options.controlId) { 7178 throw 'controlId is required'; 7179 } 7180 7181 Sys.Application.add_load(function () { 7182 var cssFile = Rock.settings.get('baseUrl') + 'Scripts/ion.rangeSlider/css/ion.rangeSlider.Rock.css'; 7183 7184 // ensure that css for rangeSlider is added to page 7185 if (!$('#rangeSliderCss').length) { 7186 $('head').append("<link id='rangeSliderCss' href='" + cssFile + "' type='text/css' rel='stylesheet' />"); 7187 } 7188 }); 7189 7190 // use https://github.com/IonDen/ion.rangeSlider/ to make a slider 7191 // see https://github.com/IonDen/ion.rangeSlider/#settings for settings 7192 $('#' + options.controlId).ionRangeSlider({ 7193 type: options.type || 'single', // Choose slider t
7193ype, could be 'single' for one handle, or 'double' for two handles 7194 min: options.min || 0, 7195 max: options.max || 100, 7196 step: options.step || 1, 7197 from: options.from || null, // for 'single' the position of the slider. for 'double' the lower position of the selected range 7198 to: options.to || null, // if 'double' the upper position of the selected range 7199 disable: options.disable || false // if 'true', the control will render as read-only 7200 }); 7201 7202 } 7203 }; 7204 7205 return exports; 7206 }()); 7207}(jQuery)); 7208; 7209(function ($) { 7210 'use strict'; 7211 window.Rock = window.Rock || {}; 7212 Rock.controls = Rock.controls || {}; 7213 7214 Rock.controls.rockCheckBoxList = (function () { 7215 7216 var exports = { 7217 clientValidate: function (validator, args) { 7218 var $checkBoxList = $(validator).closest('.js-rockcheckboxlist'); 7219 7220 var checkboxes = $checkBoxList.find('input'); 7221 var isValid = false; 7222 for (var i = 0; i < checkboxes.length; i++) { 7223 if (checkboxes[i].checked) { 7224 isValid = true; 7225 break; 7226 } 7227 } 7228 7229 if (isValid) { 7230 $checkBoxList.removeClass('has-error'); 7231 } else { 7232 $checkBoxList.addClass('has-error'); 7233 } 7234 7235 args.IsValid = isValid; 7236 } 7237 }; 7238 7239 return exports; 7240 }()); 7241}(jQuery)); 7242 7243 7244; 7245(function (Sys) { 7246 'use strict'; 7247 Sys.Application.add_load(function () { 7248 7249 var $chosenDropDowns = $('.chosen-select'); 7250 if ($chosenDropDowns.length) { 7251 // IE fix for 'chosen' causing activeElement to be a plain generic object (not a real node) after postback 7252 if (document.activeElement && !document.activeElement.nodeType) { 7253 $('body').trigger('focus'); 7254 } 7255 7256 $chosenDropDowns.chosen({ 7257 width: '100%', 7258 allow_single_deselect: true, 7259 placeholder_text_multiple: ' ', 7260 placeholder_text_single: ' ' 7261 }); 7262 7263 $chosenDropDowns.on('chosen:showing_dropdown chosen:hiding_dropdown', function (evt, params) { 7264 // update the outer modal 7265 Rock.dialogs.updateModalScrollBar(this); 7266 }); 7267 7268 var $chosenDropDownsAbsolute = $chosenDropDowns.filter('.chosen-select-absolute'); 7269 if ($chosenDropDownsAbsolute.length) { 7270 $chosenDropDownsAbsolute.on('chosen:showing_dropdown', function (evt, params) { 7271 $(this).next('.chosen-container').find('.chosen-drop').css('position', 'relative'); 7272 }); 7273 $chosenDropDownsAbsolute.on('chosen:hiding_dropdown', function (evt, params) { 7274 $(this).next('.chosen-container').find('.chosen-drop').css('position', 'absolute'); 7275 }); 7276 } 7277 } 7278 }); 7279}(Sys)); 7280; 7281(function ($, Sys) { 7282 'use strict'; 7283 window.Rock = window.Rock || {}; 7284 Rock.controls = Rock.controls || {}; 7285 7286 Rock.controls.scheduleBuilder = (function () { 7287 var exports, 7288 ScheduleBuilder = function (options) { 7289 this.id = options.id; 7290 }; 7291 7292 ScheduleBuilder.prototype.initializeEventHandlers = function () { 7293 7294 var id = this.id, 7295 $modal = $('#' + id).closest('.rock-modal'); 7296 7297 7298 $modal.find('.schedule-type').off('click').on('click',function () { 7299 var recurrenceState = $('input[class=schedule-type]:checked').data('schedule-type'); 7300 7301 if (recurrenceState === 'schedule-onetime') { 7302 $modal.find('.js-schedule-recurrence-panel').slideUp(); 7303 } else { 7304 $modal.find('.js-schedule-recurrence-panel').slideDown(); 7305 } 7306 }); 7307 7308 $modal.find('.recurrence-pattern-radio').off('click').on('click', function () { 7309 7310 var recurrencePattern = '.' + $('input[class=recurrence-pattern-radio]:checked').data('recurrence-pattern'); 7311 7312 if ($modal.find(recurrencePattern)) { 7313 7314 $modal.find('.recurrence-pattern-type').not(recurrencePattern).hide(); 7315 7316 if (recurrencePattern == ".recurrence-pattern-specific-date") { 7317 /* don't show continue-until or exclusion dates for specific date mode (since it doesn't make sense, plus ical ends up deleting them) */ 7318 $modal.find('.js-continue-until').hide(); 7319 $modal.find('.js-exclusion-dates').hide(); 7320 } 7321 else { 7322 $modal.find('.js-continue-until').show(); 7323 $modal.find('.js-exclusion-dates').show(); 7324 } 7325 7326 $(recurrencePattern).fadeIn(); 7327 } 7328 }); 7329 7330 /** Specific Dates Scripts**/ 7331 7332 // show datepicker, ok, cancel so that new date can be added to the list 7333 $modal.find('.add-specific-date').off('click').on('click', function () { 7334 $(this).hide(); 7335 $modal.find('.js-add-specific-date-group').show(); 7336 }); 7337 7338 // add new date to list when ok is clicked 7339 $modal.find('.add-specific-date-ok').off('click').on('click', function () { 7340 7341 // get date list from hidden field
7342 var dateListValues = $modal.find('.recurrence-pattern-type > input:hidden').val().split(','); 7343 7344 if (!dateListValues[0]) { 7345 // if dateList is blank, initialize as a new empty array 7346 dateListValues = []; 7347 } 7348 7349 // validate 7350 7351 // set colors back to default just in case previously marked invalid 7352 var $datepicker = $modal.find('.specific-date input'); 7353 $datepicker.css('color', ''); 7354 7355 // var checkDate = Date.parse($datepicker.val()); 7356 if ($datepicker.val() == null || $datepicker.val() == "") { 7357 // blank, don't color, just return 7358 return; 7359 } 7360 7361 var locale = window.navigator.userLanguage || window.navigator.language; 7362 moment.locale(locale); 7363 var checkDate = moment($datepicker.val(), 'l'); 7364 7365 if (!checkDate.isValid) { 7366 // invalid date entered, color red and return 7367 $datepicker.css('color', 'red'); 7368 return; 7369 } 7370 7371 var newDate = $datepicker.val(); 7372 7373 // delete newDate from list in case it is already there 7374 var index = dateListValues.indexOf(newDate); 7375 if (index >= 0) { 7376 dateListValues.splice(index, 1); 7377 } 7378 7379 // add new date to list 7380 dateListValues.push(newDate); 7381 7382 // save list back to hidden field 7383 $modal.find('.js-specific-datelist-values').val(dateListValues); 7384 7385 // rebuild the UL 7386 var dateList = $modal.find(".lstSpecificDates"); 7387 dateList.children().remove(); 7388 $.each(dateListValues, function (i, value) { 7389 // add to ul 7390 var newLi = "<li><span>" + value + "</span> <a href='#' style='display: none'> <i class='ti ti-x'></i></a></li>"; 7391 dateList.append(newLi); 7392 }); 7393 7394 $modal.find('.js-add-specific-date-group').hide(); 7395 $modal.find('.add-specific-date').show(); 7396 }); 7397 7398 // cancel out of adding a new date 7399 $modal.find('.add-specific-date-cancel').off('click').on('click', function () { 7400 $modal.find('.js-add-specific-date-group').hide(); 7401 $modal.find('.add-specific-date').show(); 7402 }); 7403 7404 // fadeIn/fadeOut the X buttons to delete dates 7405 $modal.find('.lstSpecificDates').on('mouseenter', 7406 function () { 7407 $(this).find('li a').stop(true, true).show(); 7408 }).on('mouseleave', 7409 function () { 7410 $(this).find('li a').stop(true, true).fadeOut(500); 7411 }); 7412 7413 // delete specific date from list 7414 $modal.find('.lstSpecificDates').off('click').on('click', 'li a', function () { 7415 var selectedDate = $(this).siblings().text(); 7416 7417 // get date list from hidden field 7418 var $hiddenField = $modal.find('.js-specific-datelist-values'); 7419 var dateList = $hiddenField.val().split(','); 7420 7421 // delete selectedDate 7422 var index = dateList.indexOf(selectedDate); 7423 if (index >= 0) { 7424 dateList.splice(index, 1); 7425 } 7426 7427 // save list back to hidden field 7428 $hiddenField.val(dateList); 7429 7430 // remove date from ul list 7431 var liItem = $(this).parent(); 7432 liItem.remove(); 7433 }); 7434 7435 /** Exclusion DateRanges Scripts **/ 7436 7437 // show dateRangepicker, ok, cancel so that new dateRange can be added to the list 7438 $modal.find('.add-exclusion-daterange').off('click').on('click', function () { 7439 7440 $(this).hide(); 7441 $modal.find('.js-add-exclusion-daterange-group').show(function () { 7442 Rock.controls.modal.updateSize(); 7443 }); 7444 }); 7445 7446 // add new date to list when ok is clicked 7447 $modal.find('.add-exclusion-daterange-ok').off('click').on('click', function () { 7448 7449 // get daterange list from hidden field 7450 var dateRangeListValues = $modal.find('.js-exclusion-daterange-list-values').val().split(','); 7451 if (!dateRangeListValues[0]) { 7452 // if blank, initialize as a new empty array 7453 dateRangeListValues = []; 7454 } 7455 7456 var $exclusionDateRange = $modal.find('.js-exclusion-date-range-picker input'); 7457 var newDateRange = $exclusionDateRange.first().val() + ' - ' + $exclusionDateRange.last().val(); 7458 7459 // delete newDateRange from list in case it is already there 7460 var index = dateRangeListValues.indexOf(newDateRange); 7461 if (index >= 0) { 7462 dateRangeListValues.splice(index, 1); 7463 } 7464 7465 // add new dateRange to list 7466 dateRangeListValues.push(newDateRange); 7467 7468 // save list back to hidden field 7469 $modal.find('.js-exclusion-daterange-list-values').val(dateRangeListValues); 7470 7471 // rebuild the UL 7472 var dateRangeList = $modal.find('.lstExclusionDateRanges'); 7473 dateRangeList.children().remove(); 7474 $.each(dateRangeListValues, function (i, value) { 7475 // add to ul 7476 var newLi = "<li><span>" + value + "</span><a href='#' style='display: none'><i class='ti ti-x'></i></a></li>"; 7477 dateRangeList.append(newLi);
7478 }); 7479 7480 $modal.find('.js-add-exclusion-daterange-group').hide(); 7481 $modal.find('.add-exclusion-daterange').show(); 7482 }); 7483 7484 // cancel out of adding a new dateRange 7485 $modal.find('.add-exclusion-daterange-cancel').off('click').on('click', function () { 7486 $modal.find('.js-add-exclusion-daterange-group').hide(); 7487 $modal.find('.add-exclusion-daterange').show(); 7488 }); 7489 7490 // fadeIn/fadeOut the X buttons to delete dateRanges 7491 $modal.find('.lstExclusionDateRanges').on('mouseenter', 7492 function () { 7493 $(this).find('li a').stop(true, true).show(); 7494 }).on('mouseleave', 7495 function () { 7496 $(this).find('li a').stop(true, true).fadeOut(500); 7497 }); 7498 7499 // delete dateRange from list 7500 $modal.find('.lstExclusionDateRanges').off('click').on('click', 'li a', function () { 7501 var selectedDateRange = $(this).siblings('span').text(); 7502 7503 // get dateRange list from hidden field 7504 var $hiddenField = $modal.find('.js-exclusion-daterange-list-values'); 7505 var dateRangeList = $hiddenField.val().split(','); 7506 7507 // delete selectedDateRange 7508 var index = dateRangeList.indexOf(selectedDateRange); 7509 if (index >= 0) { 7510 dateRangeList.splice(index, 1); 7511 } 7512 7513 // save list back to hidden field 7514 $hiddenField.val(dateRangeList); 7515 7516 // remove dateRange from ul list 7517 var liItem = $(this).parent(); 7518 liItem.remove(); 7519 }); 7520 7521 // validate on Save. Make sure they have at least a StartDate and Time set 7522 $modal.find('.js-modaldialog-save-link').off('click').on('click', function (event) { 7523 7524 var locale = window.navigator.userLanguage || window.navigator.language; 7525 moment.locale(locale); 7526 7527 var $datetimepicker = $modal.find('[id*="dpStartDateTime"]').find('input'), 7528 startDateValue = moment($datetimepicker.first().val(), 'l'), 7529 startTimeValue = $datetimepicker.last().val(); 7530 7531 if (!startDateValue.isValid) { 7532 $datetimepicker.parents('.form-group').first().toggleClass('has-error', 1); 7533 event.preventDefault(); 7534 return; 7535 } 7536 else { 7537 $datetimepicker.parents('.form-group').first().toggleClass('has-error', 0); 7538 } 7539 }); 7540 }; 7541 7542 exports = { 7543 cache: {}, 7544 initialize: function (options) { 7545 if (!options.id) { 7546 throw 'id is required'; 7547 } 7548 7549 var sb = new ScheduleBuilder(options); 7550 exports.cache[options.id] = sb; 7551 7552 Sys.Application.add_load(function () { 7553 sb.initializeEventHandlers(); 7554 }); 7555 } 7556 }; 7557 7558 return exports; 7559 }()); 7560}(jQuery, Sys)); 7561; 7562(function ($) { 7563 'use strict'; 7564 window.Rock = window.Rock || {}; 7565 Rock.controls = Rock.controls || {}; 7566 7567 Rock.controls.screenKeyboard = (function () { 7568 // 7569 // Handle toggling the Shift key states. 7570 // 7571 var toggleShift = function ($keyboard) { 7572 $keyboard.find('[data-command="shift"]').toggleClass('active'); 7573 7574 setAltMode($keyboard, $keyboard.find('[data-command="shift"].active').length > 0); 7575 }; 7576 7577 // 7578 // Handle setting digits to either normal value or alternate value. 7579 // 7580 var setAltMode = function ($keyboard, altMode) { 7581 $keyboard.find('.digit').each(function () { 7582 $(this).text(altMode === true ? $(this).data('alt-value') : $(this).data('value')); 7583 }); 7584 }; 7585 7586 // 7587 // Process normal digit presses. 7588 // 7589 var digitClick = function () { 7590 var $keyboard = $(this).closest('.screen-keyboard'); 7591 var $target = $('#' + $keyboard.data('target')); 7592 7593 $target.val($target.val() + $(this).text()); 7594 $target.trigger('input'); 7595 7596 if ($keyboard.find('[data-command="shift"].active').length > 0) { 7597 toggleShift($keyboard); 7598 } 7599 }; 7600 7601 // 7602 // Process special command keys. 7603 // 7604 var commandClick = function (options) { 7605 var $keyboard = $(this).closest('.screen-keyboard'); 7606 var $target = $('#' + $keyboard.data('target')); 7607 var command = $(this).data('command'); 7608 7609 if (command === 'backspace') { 7610 var val = $target.val(); 7611 if (val.length > 0) { 7612 val = val.substr(0, val.length - 1); 7613 } 7614 $target.val(val); 7615 $target.trigger('input'); 7616 } 7617 else if (command === 'clear') { 7618 $target.val(''); 7619 $target.trigger('input'); 7620 } 7621 else if (command === 'shift') { 7622 $keyboard.find('[data-command="caps"]').removeClass('active'); 7623 toggleShift($keyboard); 7624 } 7625 else if (command === 'caps') { 7626 $keyboard.find('[data-command="shift"]').removeClass('active'); 7627 $(this).toggleClass('active'); 7628 7629 setAltMode($keyboard, $(this).hasClass('active')); 7630 } 7631 else if (command === 'tab') { 7632 /* TODO: What do we do with this action? */ 7633 } 7634 else if (command === 'enter') { 7635 window.location = "javascript:__doPostBack('" + options.postback + "', 'enter')"; 7636 } 7637 }; 7638 7639 var exports = { 7640 initialize: function (options) { 7641 if (!options.id) { 7642 throw 'id is required'; 7643 } 7644 7645 var $keyboard = $('#' + options.id); 7646 7647 $keyboard.find('.digit').on('click', digitClick); 7648 $keyboard.find('.command').on('click', function () { commandClick.apply(this, [options]); }); 7649 } 7650 }; 7651 7652 return exports; 7653 }()); 7654}(jQuery)); 7655; 7656(function () { 7657 'use strict'; 7658 window.Rock = window.Rock || {}; 7659 Rock.controls = Rock.controls || {}; 7660 7661 Rock.controls.searchField = (function () { 7662 var exports, 7663 SearchField = function (options) { 7664 this.controlId = options.controlId; 7665 this.$el = $('#' + this.controlId); 7666 this.name = options.name; 7667 }; 7668 7669 SearchField.prototype = { 7670 constructor: SearchField, 7671 initialize: function () { 7672 var self = this; 7673 7674 var key = sessionStorage.getItem("com.rockrms.search"); 7675 if (key && key != '') { 7676 var $search = self.$el.parents('.smartsearch');
7677 $search.find('input:hidden').val(key); 7678 $search.find('a.dropdown-toggle > span').html($search.find('li[data-key="' + key + '"] > a').html()); 7679 } 7680 7681 this.$el.typeahead({ 7682 name: this.name, 7683 limit: 15, 7684 remote: { 7685 url: Rock.settings.get('baseUrl') + 'api/search?type=%TYPE&term=%QUERY&$top=15', 7686 replace: function (url, uriEncodedQuery) { 7687 var query = url; 7688 query = query.replace('%TYPE', self.$el.parents('.smartsearch').find('input:hidden').val()); 7689 query = query.replace('%QUERY', uriEncodedQuery); 7690 return query; 7691 } 7692 } 7693 }); 7694 7695 this.initializeEventHandlers(); 7696 }, 7697 initializeEventHandlers: function () { 7698 var self = this, 7699 search = function (term) { 7700 7701 // clear the selected item from the input as it might have HTML which w
7701ould show while the search page loads 7702 var searchControl = document.getElementById(self.controlId); 7703 searchControl.value = ''; 7704 7705 // search for data elements in the search term 7706 var $dataEl = $("<p>" + term + "</p>").find("data").first(); 7707 7708 // see if this is a universal search by looking for return-type and return-id data params 7709 var returnType = $dataEl.attr("return-type"); 7710 var returnId = $dataEl.attr("return-id"); 7711 7712 if (returnType == null || returnId == null) { 7713 var otherParams = ''; 7714 7715 // take any data attributes and add them as query parameters to the url 7716 $dataEl.each(function () { 7717 $.each(this.attributes, function () { 7718 if (this) { 7719 otherParams += '&' + this.name + '=' + encodeURIComponent(this.value); 7720 } 7721 }); 7722 }); 7723 7724 // remove any search accessories. These are html elements with the class .search-accessory 7725 var resultElement = document.createElement('div'); 7726 resultElement.innerHTML = term; 7727 //var resultElement = $("<div/>").html(term); 7728 var accessories = resultElement.getElementsByClassName('search-accessory'); 7729 7730 // remove all accessory elements 7731 while (accessories[0]) { 7732 accessories[0].parentNode.removeChild(accessories[0]); 7733 } 7734 7735 // remove any html from the search term before putting it in the url 7736 var targetTerm = resultElement.textContent.trim(); 7737 7738 var keyVal = self.$el.parents('.smartsearch').find('input:hidden').val(), 7739 $li = self.$el.parents('.smartsearch').find('li[data-key="' + keyVal + '"]'), 7740 targetUrl = $li.attr('data-target'), 7741 url = Rock.settings.get('baseUrl') + targetUrl.replace('{0}', encodeURIComponent(targetTerm)); 7742 7743 if (otherParams != '') { 7744 if (url.indexOf('?') > -1) { 7745 url += otherParams 7746 } else { 7747 url += otherParams.replace(/^&/, '?') 7748 } 7749 } 7750 7751 window.location = url; 7752 } else { 7753 // universal search uses returnType and returnId, so build the url for that 7754 var keyVal = self.$el.parents('.smartsearch').find('input:hidden').val(), 7755 $li = self.$el.parents('.smartsearch').find('li[data-key="' + keyVal + '"]'), 7756 targetUrl = $li.attr('data-target'), 7757 url = Rock.settings.get('baseUrl') + targetUrl.replace('{0}', encodeURIComponent(returnType) + "/" + encodeURIComponent(returnId)); 7758 7759 window.location = url; 7760 } 7761 }; 7762 7763 // Listen for typeahead's custom events and trigger search when hit 7764 this.$el.on('typeahead:selected typeahead:autocompleted', function (e, obj, name) { 7765 search(obj.value); 7766 }); 7767 7768 // Listen for the ENTER key being pressed while in the search box and trigger search when hit 7769 this.$el.on('keydown', function (e) { 7770 if (e.keyCode === 13) { 7771 e.preventDefault(); 7772 return false; 7773 } 7774 }); 7775 this.$el.on('keyup', function (e) { 7776 if (e.keyCode === 13 && "" !== $(this).val().trim() ) { 7777 search($(this).val()); 7778 } 7779 }); 7780
7781 // Wire up "change" handler for search type "dropdown menu" 7782 this.$el.parents('.smartsearch').find('.dropdown-menu a').on('click', function () { 7783 var $this = $(this), 7784 text = $this.html(); 7785 7786 var key = $this.parent().attr('data-key'); 7787 sessionStorage.setItem("com.rockrms.search", key); 7788 7789 $this.parents('.dropdown-menu').siblings('.navbar-link').find('span').html(text); 7790 self.$el.parents('.smartsearch').find('input:hidden').val(key) 7791 }); 7792 } 7793 }; 7794 7795 exports = { 7796 defaults: { 7797 controlId: null, 7798 name: 'search' 7799 }, 7800 controls: {}, 7801 initialize: function (options) { 7802 var searchField, 7803 settings = $.extend({}, exports.defaults, options); 7804 7805 if (!settings.controlId) throw 'controlId is required'; 7806 7807 if (!exports.controls[settings.controlId]) { 7808 searchField = new SearchField(settings); 7809 exports.controls[settings.controlId] = searchField; 7810 } else { 7811 searchField = exports.controls[settings.controlId]; 7812 } 7813 7814 // Delay initialization until after the DOM is ready 7815 $(function () { 7816 searchField.initialize(); 7817 }); 7818 } 7819 }; 7820 7821 return exports; 7822 }()); 7823}()); 7824; 7825(function ($) { 7826 'use strict'; 7827 window.Rock = window.Rock || {}; 7828 Rock.controls = Rock.controls || {}; 7829 7830 Rock.controls.slidingDateRangePicker = (function () { 7831 var exports = { 7832 initialize: function (options) { 7833 if (!options.id) { 7834 throw 'id is required'; 7835 } 7836 7837 var $picker = $('#' + options.id); 7838 7839 // the dropdown with current, last, previous, daterange in it 7840 var $select = $picker.find('.js-slidingdaterange-select'); 7841 7842 showHideControls($picker, $select); 7843 updateDateRangeInfo($picker); 7844 7845 $select.on('change', function () { 7846 showHideControls($picker, $select); 7847 updateDateRangeInfo($picker); 7848 }); 7849 7850 $('.js-number, .js-time-units-singular, .js-time-units-plural, .js-slidingdaterange-select, .js-lower, .js-upper', $picker).on('change', function () { 7851 updateDateRangeInfo($picker); 7852 }); 7853 7854 $('.js-number', $picker).on('keyup', function () { 7855 updateDateRangeInfo($picker); 7856 }); 7857 } 7858 }, 7859 showHideControls = function ($picker, $select) { 7860 var selectedValue = $select.val(); 7861 var isLast = selectedValue == '0'; 7862 var isCurrent = selectedValue == '1'; 7863 var isDateRange = selectedValue == '2'; 7864 var isPrevious = selectedValue == '4'; 7865 var isNext = selectedValue == '8'; 7866 var isUpcoming = selectedValue == '16'; 7867 $picker.find('.js-number').toggle(isLast || isPrevious || isNext || isUpcoming); 7868 $picker.find('.js-time-units-singular').toggle(isCurrent); 7869 $picker.find('.js-time-units-plural').toggle(isLast || isPrevious || isNext || isUpcoming); 7870 $picker.find('.js-time-units-date-range').toggle(isDateRange); 7871 var $pickerContainer = $picker.closest('.js-slidingdaterange-container'); 7872 if (isLast || isPrevious || isNext || isUpcoming || isCurrent) { 7873 $pickerContainer.find('.js-slidingdaterange-info').css("display", "inline"); 7874 } else { 7875 $pickerContainer.find('.js-slidingdaterange-info').hide(); 7876 } 7877 }, 7878 updateDateRangeInfo = function ($picker) { 7879 var timeUnitType = 0; 7880 if ($picker.find('.js-time-units-singular').is(':visible')) { 7881 timeUnitType = $picker.find('.js-time-units-singular').val(); 7882 } else { 7883 timeUnitType = $picker.find('.js-time-units-plural').val(); 7884 } 7885 7886 var numberOf = $picker.find('.js-number').val(); 7887 7888 var $pickerContainer = $picker.closest('.js-slidingdaterange-container'); 7889 7890 var dateRangeString = ''; 7891 var $select = $picker.find('.js-slidingdaterange-select'); 7892 var selectedValue = $select.val(); 7893 if (selectedValue == '2') { 7894 var startPicker = $pickerContainer.find('.js-lower'); 7895 var startDate = startPicker.find('.form-control').val(); 7896 7897 var endPicker = $pickerContainer.find('.js-upper'); 7898 var endDate = endPicker.find('.form-control').val(); 7899 7900 if (startDate != '' && endDate != '') { 7901 dateRangeString = '&startDate=' + startDate + '&endDate=' + endDate; 7902 } 7903 } 7904 7905 if (selectedValue != '2' || dateRangeString != '') { 7906 var getDateRangeUrl = Rock.settings.get('baseUrl') + 'api/Utility/CalculateSlidingDateRange?slidingDateRangeType=' + $select.val() + '&timeUnitType=' + timeUnitType + '&number=' + numberOf + dateRangeString; 7907 $.get(getDateRangeUrl, function (r) { 7908 $pickerContainer.find('.js-slidingdaterange-info').text(r); 7909 }); 7910 7911 var getTextValueUrl = Rock.settings.get('baseUrl') + 'api/Utility/GetSlidingDateRangeTextValue?slidingDateRangeType=' + $select.val() + '&timeUnitType=' + timeUnitType + '&number=' + numberOf + dateRangeString; 7912 $.get(getTextValueUrl, function (r) { 7913 $pickerContainer.find('.js-slidingdaterange-text-value').val(r).change(); 7914 }); 7915 } 7916 }; 7917 7918 return exports; 7919 }()); 7920}(jQuery)); 7921; 7922(function ($) { 7923 'use strict'; 7924 window.Rock = window.Rock || {}; 7925 Rock.controls = Rock.controls || {}; 7926 7927 Rock.controls.tagList = (function () { 7928 var TagList = function (options) { 7929 this.controlId = options.controlId; 7930 this.entityTypeId = options.entityTypeId;
7931 this.currentPersonId = options.currentPersonId; 7932 this.entityGuid = options.entityGuid; 7933 this.entityQualifierColumn = options.entityQualifierColumn; 7934 this.entityQualifierValue = options.entityQualifierValue; 7935 this.preventTagCreation = options.preventTagCreation; 7936 this.delaySave = options.delaySave; 7937 this.categoryGuid = options.categoryGuid; 7938 this.includeInactive = options.includeInactive; 7939 }, 7940 exports; 7941 7942 TagList.prototype.verifyTag = function (tagName) { 7943 // Since `verifyTag` is being called by the jQuery lib, `this` 7944 // is the current TagList DOM element. Get the HTML ID and fetch 7945 // from the cache. 7946 var tagList = exports.tagLists[$(this).attr('id')]; 7947 if (! /^((?!<)(?!>)(?!%)(?!&).)*$/.test(tagName)) { 7948 Rock.dialogs.alert("Invalid characters have been entered for the tag name. Angle brackets, percent, and ampersand are not allowed."); 7949 $('#' + tagList.controlId).removeTag(tagName); 7950 return; 7951 } 7952 7953 var restUrl = Rock.settings.get('baseUrl') + 'api/tags'; 7954 restUrl += '?entityTypeId=' + tagList.entityTypeId; 7955 restUrl += '&ownerId=' + tagList.currentPersonId; 7956 restUrl += '&name=' + encodeURIComponent(tagName); 7957 restUrl += '&includeInactive' + tagList.includeInactive.toString(); 7958 7959 if (tagList.entityQualifierColumn) { 7960 restUrl += '&entityQualifier=' + tagList.entityQualifierColumn; 7961 } 7962 7963 if (tagList.entityQualifierValue) { 7964 restUrl += '&entityQualifierValue=' + tagList.entityQualifierValue; 7965 } 7966 7967 if (tagList.categoryGuid && tagList.categoryGuid != '') { 7968 restUrl += '&categoryGuid=' + tagList.categoryGuid; 7969 } 7970 7971 $.ajax({
7972 url: restUrl, 7973 statusCode: { 7974 404: function () { 7975 if (tagList.preventTagCreation) { 7976 $('#' + tagList.controlId).removeTag(tagName); 7977 } 7978 else { 7979 Rock.dialogs.confirm('A tag called "' + $('<div/>').text(tagName).html() + '" does not exist. Do you want to create a new personal tag?', function (result) { 7980 if (result) { 7981 tagList.addTag(tagName); 7982 } else { 7983 $('#' + tagList.controlId).removeTag(tagName); 7984 } 7985 }); 7986 } 7987 }, 7988 403: function () { 7989 Rock.dialogs.alert("Invalid characters have been entered for the tag name. Angle brackets, percent, and ampersand are not allowed."); 7990 $('#' + tagList.controlId).removeTag(tagName); 7991 }, 7992 200: function () { 7993 tagList.addTag(tagName); 7994 } 7995 } 7996 }); 7997 }; 7998 7999 TagList.prototype.addTag = function (tagName) { 8000 // `addTag` is invoked by `verifyTag` on an instance of a `TagList` object. 8001 // `this` is the current TagList instance in scope. 8002 8003 var tagList = this, 8004 restUrl = Rock.settings.get('baseUrl') + 'api/taggeditems' 8005 restUrl += '?entityTypeId=' + tagList.entityTypeId; 8006 restUrl += '&ownerId=' + tagList.currentPersonId; 8007 restUrl += '&entityGuid=' + tagList.entityGuid; 8008 restUrl += '&name=' + encodeURIComponent(tagName); 8009 restUrl += '&includeInactive' + tagList.includeInactive.toString(); 8010 8011 if (tagList.categoryGuid && tagList.categoryGuid != '') { 8012 restUrl += '&categoryGuid=' + this.categoryGuid; 8013 } 8014 8015 // only attempt to add tag if an entityGuid exists 8016 if (!tagList.delaySave) { 8017 if (tagList.entityQualifierColumn) { 8018 restUrl += '/' + tagList.entityQualifierColumn; 8019 } 8020 8021 if (tagList.entityQualifierValue) { 8022 restUrl += '/' + tagList.entityQualifierValue; 8023 } 8024 8025 $.ajax({
8026 type: 'POST', 8027 url: restUrl, 8028 error: function (xhr, status, error) { 8029 console.log('AddTag() status: ' + status + ' [' + error + ']: ' + xhr.responseText); 8030 } 8031 }); 8032 } 8033 }; 8034 8035 TagList.prototype.removeTag = function (tagName) { 8036 8037 // Since `removeTag` is being called by the jQuery lib, `this` 8038 // is the current TagList DOM element. Get the HTML ID and fetch 8039 // from the cache. 8040 var tagList = exports.tagLists[$(this).attr('id')], 8041 restUrl = Rock.settings.get('baseUrl') + 'api/taggeditems'; 8042 8043 // only attempt to remove tag if an entityGuid exists 8044 if (!tagList.delaySave) { 8045 8046 restUrl += '?entityTypeId=' + tagList.entityTypeId; 8047 restUrl += '&ownerId=' + tagList.currentPersonId; 8048 restUrl += '&entityGuid=' + tagList.entityGuid; 8049 restUrl += '&name=' + encodeURIComponent(tagName); 8050 restUrl += '&includeInactive' + tagList.includeInactive.toString(); 8051 8052 if (tagList.entityQualifierColumn) { 8053 restUrl += '&entityQualifier=' + tagList.entityQualifierColumn; 8054 } 8055 8056 if (tagList.entityQualifierValue) { 8057 restUrl += '&entityQualifierValue=' + tagList.entityQualifierValue; 8058 } 8059 8060 if (tagList.categoryGuid && tagList.categoryGuid != '') { 8061 restUrl += '&categoryGuid=' + tagList.categoryGuid; 8062 } 8063 8064 $.ajax({
8065 type: 'DELETE', 8066 url: restUrl, 8067 context: { 8068 tagName: tagName, 8069 tagsInput: this 8070 }, 8071 error: function (xhr, status, error) { 8072 if (xhr && xhr.status == 404) { 8073 // already deleted 8074 return; 8075 } 8076 8077 Rock.dialogs.alert('Unable to remove tag: ' + error); 8078 8079 // put the tag back in (in alpha order, case-insensitive) 8080 var tagsCommaList = $(this.tagsInput).val() + ',' + this.tagName 8081 8082 tagsCommaList = tagsCommaList.split(',').sort(function (a, b) { 8083 return a.toLowerCase().localeCompare(b.toLowerCase()); 8084 }).join(','); 8085 8086 $(this.tagsInput).importTags(tagsCommaList); 8087 return false; 8088 } 8089 }); 8090 } 8091 8092 }; 8093 8094 TagList.prototype.initialize = function () { 8095 8096 var restUrl = Rock.settings.get('baseUrl') + 'api/Tags/AvailableNames'; 8097 restUrl += '?entityTypeId=' + this.entityTypeId; 8098 restUrl += '&ownerId=' + this.currentPersonId; 8099 restUrl += '&entityGuid=' + this.entityGuid; 8100 restUrl += '&includeInactive' + this.includeInactive.toString(); 8101 8102 if (this.entityQualifierColumn) { 8103 restUrl += '&entityQualifier=' + this.entityQualifierColumn; 8104 } 8105 if (this.entityQualifierValue) { 8106 restUrl += '&entityQualifierValue=' + this.entityQualifierValue; 8107 } 8108 if (this.categoryGuid && this.categoryGuid != '') { 8109 restUrl += '&categoryGuid=' + this.categoryGuid; 8110 } 8111 8112 $('ul.ui-autocomplete').css({ 'width': '300px' }); 8113 8114 $('#' + this.controlId).tagsInput({ 8115 autocomplete_url: function (request, response) { 8116 $.ajax({
8117 url: restUrl + '&name=' + request.term, 8118 dataType: 'json', 8119 success: function (data, status, xhr) { 8120 response($.map(data, function (item) { 8121 return { 8122 value: item.Name, 8123 class: !item.OwnerId ? 'system' : 'personal' 8124 }; 8125 })); 8126 }, 8127 error: function (xhr, status, error) { 8128 console.log('availablenames status: ' + status + ' [' + error + ']: ' + xhr.reponseText); 8129 } 8130 }); 8131 }, 8132 autoCompleteAppendTo: 'div.tag-wrap', 8133 autoCompleteMessages: { 8134 noResults: function () {}, 8135 results: function () {} 8136 }, 8137 height: 'auto', 8138 width: '100%', 8139 interactive: true, 8140 defaultText: 'add tag', 8141 removeWithBackspace: false, 8142 onAddTag: this.verifyTag, 8143 onRemoveTag: this.removeTag 8144 }); 8145 }; 8146 8147 exports = { 8148 tagLists: {}, 8149 initialize: function (options) { 8150 if (!options.controlId) throw 'controlId must be set.'; 8151 if (!options.entityTypeId) throw 'entityTypeId must be set.'; 8152 if (!options.currentPersonId) throw 'currentPersonId must be set'; 8153 if (!options.entityGuid) throw 'entityGuid must be set'; 8154 8155 var tagList = new TagList(options); 8156 tagList.options = options; 8157 exports.tagLists[options.controlId] = tagList; 8158 tagList.initialize(); 8159 } 8160 }; 8161 8162 return exports; 8163 }()); 8164}(jQuery)); 8165; 8166(function ($) { 8167 'use strict'; 8168 window.Rock = window.Rock || {}; 8169 Rock.controls = Rock.controls || {}; 8170 8171 Rock.controls.taskActivityProgressReporter = (function () { 8172 const TaskActivityProgressReporter = function (options) { 8173 if (!Rock.RealTime) { 8174 throw new Error("realtime.js must be included first."); 8175 } 8176 8177 if (!options.controlId) { 8178 throw new Error("controlId is required."); 8179 } 8180 8181 this.controlId = options.controlId; 8182 this.connectionId = undefined; 8183 8184 this.connect(); 8185 } 8186 8187 TaskActivityProgressReporter.prototype.getTaskId = function () { 8188 const control = document.getElementById(this.controlId); 8189 8190 if (!control) { 8191 return null; 8192 } 8193 8194 return control.getAttribute("data-task-id"); 8195 }; 8196 8197 TaskActivityProgressReporter.prototype.connect = async function () { 8198 this.topic = await Rock.RealTime.getTopic("Rock.RealTime.Topics.TaskActivityProgressTopic"); 8199 8200 this.topic.on("taskStarted", this.onTaskStarted.bind(this)); 8201 this.topic.on("taskCompleted", this.onTaskCompleted.bind(this)); 8202 this.topic.on("updateTaskProgress", this.onUpdateTaskProgress.bind(this)); 8203 8204 const $connectionId = $(`#${this.controlId} [id$='_hfConnectionId']`); 8205 8206 $connectionId.val(this.topic.connectionId || ""); 8207 }; 8208 8209 TaskActivityProgressReporter.prototype.onTaskStarted = function (status) { 8210 // Intentionally left blank for now. 8211 }; 8212 8213 TaskActivityProgressReporter.prototype.onTaskCompleted = function (status) { 8214 const taskId = this.getTaskId(); 8215 8216 if (status.taskId !== taskId) { 8217 return; 8218 } 8219 8220 const $preparing = $(`#${this.controlId} .js-preparing`); 8221 const $progress = $(`#${this.controlId} .js-progress-div`); 8222 const $results = $(`#${this.controlId} .js-results`); 8223 8224 $results.removeClass("alert-danger").removeClass("alert-warning").removeClass("alert-success"); 8225 8226 if (status.errors && status.errors.length > 0) { 8227 console.error(status.errors); 8228 $results.addClass("alert-danger"); 8229 } 8230 else if (status.warnings && status.warnings.length > 0) { 8231 console.warn(status.warnings); 8232 $results.addClass("alert-warning"); 8233 } 8234 else { 8235 $results.addClass("alert-success"); 8236 } 8237 8238 $results.text(status.message).slideDown(); 8239 $preparing.slideUp();
8240 $progress.slideUp(); 8241 }; 8242 8243 TaskActivityProgressReporter.prototype.onUpdateTaskProgress = function (progress) { 8244 const taskId = this.getTaskId(); 8245 8246 if (progress.taskId !== taskId) { 8247 return; 8248 } 8249 8250 const $preparing = $(`#${this.controlId} .js-preparing`); 8251 const $progress = $(`#${this.controlId} .js-progress-div`); 8252 const $bar = $(`#${this.controlId} .js-progress-bar`); 8253 8254 $bar.prop("aria-valuenow", progress.completionPercentage); 8255 $bar.prop("aria-valuemax", "100"); 8256 $bar.css("width", `${progress.completionPercentage}%`); 8257 8258 if (progress.message) { 8259 $bar.text(progress.message); 8260 } 8261 else { 8262 $bar.text(`${progress.completionPercentage}%`); 8263 } 8264 8265 $progress.slideDown(); 8266 $preparing.slideUp(); 8267 }; 8268 8269 const reporters = {}; 8270 8271 const exports = { 8272 initialize(options) { 8273 if (!options.controlId) { 8274 throw new Error("id is required."); 8275 } 8276 8277 if (reporters[options.controlId]) { 8278 return; 8279 } 8280 8281 reporters[options.controlId] = new TaskActivityProgressReporter(options); 8282 } 8283 }; 8284 8285 return exports; 8286 }()); 8287}(jQuery)); 8288; 8289(function ($) { 8290 'use strict'; 8291 window.Rock = window.Rock || {}; 8292 Rock.controls = Rock.controls || {}; 8293 8294 Rock.controls.timePicker = (function () { 8295 var exports = { 8296 initialize: function (options) { 8297 if (!options.id) { 8298 throw 'id is required'; 8299 } 8300 8301 var $tp = $('#' + options.id); 8302 8303 // bootstrap-timepicker requires that the parent div have bootstrap-timepicker, input-append classes 8304 $tp.closest('div').addClass('bootstrap-timepicker').addClass('input-append'); 8305 8306 // uses https://github.com/jdewit/bootstrap-timepicker 8307 $tp.timepicker({ 8308 defaultTime: false 8309 }); 8310 8311 $tp.closest('.js-timepicker-input').find('.js-timepicker-clear').on('click', function () { 8312 $tp.timepicker('clear'); 8313 }); 8314 } 8315 }; 8316 8317 return exports; 8318 }()); 8319}(jQuery)); 8320; 8321(function ($) { 8322 'use strict'; 8323 window.Rock = window.Rock || {}; 8324 Rock.controls = Rock.controls || {}; 8325 8326 Rock.controls.toggleButton = (function () { 8327 var exports = { 8328 initialize: function (options) { 8329 if (!options.id) { 8330 throw 'id is required'; 8331 } 8332 8333 // uses pattern from http://www.bootply.com/92189 8334 8335 $('#' + options.id + ' .btn-toggle').on('click', function (e) { 8336 8337 e.stopImmediatePropagation(); 8338 8339 $(this).find('.btn').toggleClass('active'); 8340 8341 if (options.activeButtonCssClass && $(this).find('.' + options.activeButtonCssClass).length > 0) { 8342 $(this).find('.btn').toggleClass(options.activeButtonCssClass); 8343 } 8344 8345 if (options.onButtonCssClass) { 8346 $(this).find('.js-toggle-on').toggleClass(options.onButtonCssClass); 8347 } 8348 8349 if (options.offButtonCssClass) { 8350 $(this).find('.js-toggle-off').toggleClass(options.offButtonCssClass); 8351 } 8352 8353 $(this).parent().find('.js-toggle-checked').val($(this).find('.js-toggle-on').hasClass('active')); 8354 8355 }); 8356 8357 } 8358 }; 8359 8360 return exports; 8361 }()); 8362}(jQuery)); 8363; 8364(function ($) { 8365 'use strict'; 8366 window.Rock = window.Rock || {}; 8367 Rock.controls = Rock.controls || {}; 8368 8369 Rock.controls.universalItemSearchPicker = (function () { 8370 var UniversalItemSearchPicker = function (options) { 8371 this.controlId = options.controlId; 8372 this.restUrl = options.restUrl; 8373 this.context = options.context; 8374 this.areDetailsAlwaysVisible = options.areDetailsAlwaysVisible; 8375 this.iScroll = null; 8376 this.$pickerControl = $('#' + this.controlId);
8377 this.$pickerScrollContainer = this.$pickerControl.find('.js-universalitemsearchpicker-scroll-container'); 8378 }; 8379 8380 UniversalItemSearchPicker.prototype.initializeEventHandlers = function () { 8381 var controlId = this.controlId; 8382 var restUrl = this.restUrl; 8383 var context = this.context; 8384 var areDetailsAlwaysVisible = this.areDetailsAlwaysVisible; 8385 8386 var $pickerControl = this.$pickerControl; 8387 var $searchFields = $pickerControl.find('input.js-universalitemsearchpicker-search-field'); 8388 var $searchFieldName = $pickerControl.find('input.js-universalitemsearchpicker-search-name'); 8389 var $searchFieldIncludeInactive = $pickerControl.find("input.js-include-inactive"); 8390 var $searchResults = $pickerControl.find('.js-universalitemsearchpicker-searchresults'); 8391 var $pickerToggle = $pickerControl.find('.js-universalitemsearchpicker-toggle'); 8392 var $pickerMenu = $pickerControl.find('.js-universalitemsearchpicker-menu'); 8393 var $pickerSelect = $pickerControl.find('.js-universalitemsearchpicker-select'); 8394 var $pickerSelectNone = $pickerControl.find('.js-picker-select-none'); 8395 var $pickerItemValue = $pickerControl.find('.js-item-value'); 8396 var $pickerItemName = $pickerControl.find('.js-item-name'); 8397 var $pickerCancel = $pickerControl.find('.js-universalitemsearchpicker-cancel'); 8398 var promise = null; 8399 var lastSelectedItemValue = null; 8400 8401 var autoCompletes = $searchFields.autocomplete({ 8402 source: function (request, response) { 8403 8404 var search = { 8405 value: $searchFieldName.val() 8406 }; 8407 8408 // make sure that at least one of the search fields has 3 chars in it 8409 if (search.value.length < 3) { 8410 return; 8411 } 8412 8413 // abort any searches that haven't returned yet, so that we don't get a pile of results in random order 8414 if (promise && promise.state() === 'pending') { 8415 promise.abort(); 8416 } 8417 8418 var searchBag = { 8419 context: context, 8420 value: search.value, 8421 isInactiveIncluded: $searchFieldIncludeInactive && $searchFieldIncludeInactive.is(":checked") == true 8422 }; 8423 8424 // set the timeout to 20 seconds, just in case it takes a long time to search 8425 promise = $.ajax({ 8426 url: restUrl, 8427 method: "POST", 8428 data: JSON.stringify(searchBag), 8429 contentType: "application/json", 8430 timeout: 20000, 8431 dataType: 'json' 8432 }); 8433 8434 // Display a wait indicator to show that the search is now running. 8435 if ($('.js-searching-notification').length == 0) { 8436 $searchResults.prepend('<i class="ti ti-refresh ti-spin margin-l-md js-searching-notification" style="display: none; opacity: .4;"></i>'); 8437 } 8438 $('.js-searching-notification').fadeIn(800); 8439 8440 promise.done(function (data) { 8441 $searchResults.html(''); 8442 8443 response(data); 8444 8445 exports.universalItemSearchPickers[controlId].updateScrollbar(); 8446 }); 8447 8448 promise.fail(function (xhr, status, error) { 8449 console.log(status + ' [' + error + ']: ' + xhr.responseText); 8450 var errorCode = xhr.status; 8451 if (errorCode == 401) { 8452 $searchResults.html("<li class='text-danger'>Sorry, you're not authorized to search.</li>"); 8453 } 8454 8455 $('.js-searching-notification').remove(); 8456 }); 8457 }, 8458 // Set minLength to 0, but check that at least one field has 3 chars before fetching from REST. 8459 // To minimize load on the server, don't trigger the search until a reasonable delay after the last keypress. 8460 minLength: 0, 8461 delay: 750, 8462 html: true, 8463 appendTo: $searchResults, 8464 pickerControlId: controlId, 8465 messages: { 8466 noResults: function () { }, 8467 results: function () { } 8468 } 8469 }); 8470 8471 var autoCompleteCustomRenderItem = function ($ul, item) { 8472 // override jQueryUI autocomplete's _renderItem so that we can do HTML for the ListItems 8473 // derived from http://github.com/scottgonzalez/jquery-ui-extensions 8474
8475 var $div = $('<div/>').attr('class', 'radio'); 8476 8477 var $labelText = $('<span/>') 8478 .addClass('label-text d-flex gap') 8479 .append($('<span/>').addClass('flex-grow-1').text(item.title)); 8480 8481 if (item.labels) { 8482 var $labels = $('<span/>').addClass('d-flex gap'); 8483 8484 for (let labelIndex = 0; labelIndex < item.labels.length; labelIndex++) { 8485 $('<span/>') 8486 .text(item.labels[labelIndex].text) 8487 .addClass('label label-' + item.labels[labelIndex].value) 8488 .appendTo($labels); 8489 } 8490 8491 $labelText.append($labels); 8492 } 8493 8494 var $label = $('<label/>') 8495 .append($labelText) 8496 .prependTo($div); 8497 8498 $('<input type="radio" name="item-id" />') 8499 .attr('value', item.Id) 8500 .prependTo($label); 8501 8502 var $li = $('<li/>') 8503 .addClass('picker-select-item js-picker-select-item') 8504 .css('list-style', 'none') 8505 .attr('data-item-value', item.value) 8506 .attr('data-item-name', item.title) 8507 .html($div); 8508 8509 var $resultSection = $(this.options.appendTo); 8510 8511 var $itemDetailsDiv = $('<div/>') 8512 .addClass('picker-select-item-details js-picker-select-item-details clearfix'); 8513 8514 if (item.description) { 8515 var $descriptionDiv = $('<div/>'); 8516 8517 $descriptionDiv.addClass("picker-select-item-description mb-2"); 8518 $descriptionDiv.text(item.description); 8519 8520 $itemDetailsDiv.append($descriptionDiv); 8521 } 8522 8523 if (item.details) { 8524 for (let detailIndex = 0; detailIndex < item.details.length; detailIndex++) { 8525 var detail = item.details[detailIndex]; 8526 var $detailDl = $('<dl/>').addClass("d-flex"); 8527 $detailDl.append($("<dt/>").addClass("mr-2 text-nowrap").text(detail.value)); 8528 $detailDl.append($("<dd/>").text(detail.text)); 8529 8530 $itemDetailsDiv.append($detailDl); 8531 } 8532 } 8533 8534 if (!areDetailsAlwaysVisible) { 8535 $itemDetailsDiv.hide(); 8536 } 8537 8538 $itemDetailsDiv.appendTo($li); 8539 8540 if (item.isInactive) { 8541 $li.addClass('is-inactive'); 8542 } 8543 8544 return $resultSection.append($li); 8545 } 8546 8547 // each search field has its own autocomplete object, so we'll need override with our custom _renderItem to each 8548 $.each(autoCompletes, function (a, b, c) { 8549 var autoComplete = $(autoCompletes[a]).data('ui-autocomplete'); 8550 8551 // Debugging: override close to prevent it canceling when loosing focus when debugging 8552 // autoComplete.close = function () { }; 8553 8554 autoComplete._renderItem = autoCompleteCustomRenderItem; 8555 }); 8556 8557 $pickerToggle.on('click', function (e) { 8558 e.preventDefault(); 8559 $(this).toggleClass("active"); 8560 $pickerMenu.toggle(0, function () { 8561 exports.universalItemSearchPickers[controlId].updateScrollbar(); 8562 $searchFieldName.trigger('focus'); 8563 }); 8564 }); 8565 8566 $pickerControl.on('click', '.js-picker-select-item', function (e) { 8567 if (e.type == 'click' && $(e.target).is(':input') == false) { 8568 // only process the click event if it has bubbled up to the input tag 8569 return; 8570 } 8571 8572 e.stopPropagation(); 8573 8574 var $selectedItem = $(this).closest('.js-picker-select-item'); 8575 var $itemDetails = $selectedItem.find('.js-picker-select-item-details'); 8576 8577 var selectedItemValue = $selectedItem.attr('data-item-value'); 8578 8579 if ($itemDetails.is(':visible')) { 8580 if (selectedItemValue == lastSelectedItemValue && e.type == 'click') { 8581 // if they are clicking the same item twice in a row 8582 // (and the details are done expanding), assume that's 8583 // the one they want to pick 8584 $pickerSelect.trigger('click'); 8585 } else { 8586 // if it is already visible but isn't the same one twice, just leave it open 8587 } 8588 } 8589 8590 lastSelectedItemValue = selectedItemValue; 8591 8592 showItemDetails($selectedItem.find('.picker-select-item-details:hidden')); 8593 }); 8594 8595 var showItemDetails = function ($itemDetails) { 8596 if ($itemDetails.length) { 8597 $itemDetails.slideDown(function () { 8598 exports.universalItemSearchPickers[controlId].updateScrollbar(); 8599 }); 8600 } 8601 } 8602 8603 $pickerControl.on('mouseenter', 8604 function () { 8605 // only show the X if there is something picked 8606 if (($pickerItemValue.val() || '') !== '') { 8607 $pickerSelectNone.addClass('show-hover'); 8608 } 8609 }); 8610 8611 $pickerCancel.on('click', function () { 8612 clearSearchFields(); 8613 $pickerMenu.slideUp(function () { 8614 exports.universalItemSearchPickers[controlId].updateScrollbar(); 8615 }); 8616 }); 8617 8618 $pickerSelectNone.on('click', function (e) { 8619 // prevent the click from bubbling up to the pickerControl click event 8620 e.preventDefault(); 8621 e.stopPropagation(); 8622 8623 $pickerItemValue.val(''); 8624 $pickerItemName.val('');
8625 // run onclick event from the button 8626 $(this).trigger('onclick'); 8627 }); 8628 8629 // disable the enter key : this will prevent the enter key from clearing the loaded search query. 8630 $pickerControl.on('keypress', function (e) { 8631 if (e.which == 13) { 8632 return false; 8633 } 8634 }); 8635 8636 var setSelectedItem = function (selectedValue, selectedText) { 8637 var selectedItemLabel = $pickerControl.find('.js-universalitemsearchpicker-selecteditem-label'); 8638 8639 $pickerItemValue.val(selectedValue); 8640 $pickerItemName.val(selectedText); 8641 8642 selectedItemLabel.val(selectedValue); 8643 selectedItemLabel.text(selectedText); 8644 8645 $pickerMenu.slideUp(); 8646 } 8647 8648 var clearSearchFields = function () { 8649 $searchFieldName.val(''); 8650 } 8651 8652 $pickerSelect.on('click', function () { 8653 var $selectedItem = $pickerControl.find('input:checked').closest('.js-picker-select-item'); 8654 var selectedItemValue = $selectedItem.attr('data-item-value'); 8655 var selectedText = $selectedItem.attr('data-item-name'); 8656 8657 setSelectedItem(selectedItemValue, selectedText); 8658 clearSearchFields(); 8659 8660 // Fire the postBack for the Select button. 8661 var postBackUrl = $pickerSelect.prop('href'); 8662 if (postBackUrl) { 8663 window.location = postBackUrl; 8664 } 8665 }); 8666 }; 8667 8668 UniversalItemSearchPicker.prototype.updateScrollbar = function () { 8669 var self = this; 8670 8671 // first, update this control's scrollbar, then the modal's 8672 8673 if (self.$pickerScrollContainer.is(':visible')) { 8674 if (self.iScroll) { 8675 self.iScroll.refresh(); 8676 } 8677 } 8678 8679 // update the outer modal scrollbar 8680 Rock.dialogs.updateModalScrollBar(this.controlId); 8681 } 8682 8683 UniversalItemSearchPicker.prototype.initialize = function () { 8684 this.iScroll = new IScroll($('.viewport', this.$pickerControl)[0], { 8685 mouseWheel: true, 8686 indicators: { 8687 el: $('.track', this.$pickerScrollContainer)[0], 8688 interactive: true, 8689 resize: false, 8690 listenY: true, 8691 listenX: false, 8692 }, 8693 click: false, 8694 preventDefaultException: { tagName: /.*/ } 8695 }); 8696 8697 this.initializeEventHandlers(); 8698 }; 8699 8700 var exports = { 8701 universalItemSearchPickers: {}, 8702 findControl: function (controlId) { 8703 return exports.universalItemSearchPickers[controlId]; 8704 }, 8705 initialize: function (options) { 8706 if (!options.controlId) throw '`controlId` is required.'; 8707 if (!options.restUrl) throw '`restUrl` is required.'; 8708 8709 var picker = new UniversalItemSearchPicker(options); 8710 exports.universalItemSearchPickers[options.controlId] = picker; 8711 picker.initialize(); 8712 } 8713 }; 8714 return exports; 8715 }()); 8716}(jQuery)); 8717; 8718(function ($) { 8719 'use strict'; 8720 window.Rock = window.Rock || {}; 8721 Rock.controls = Rock.controls || {}; 8722 8723 Rock.controls.util = (function () { 8724 var exports = { 8725 googleMapsIsLoaded: function () { 8726 // trigger googleMapsIsLoaded so that controls (like the geoPicker) know about it 8727 $(window).trigger('googleMapsIsLoaded'); 8728 }, 8729 loadGoogleMapsApi: function (apiSrc) { 8730 // ensure that js for googleMapsApi is added to page 8731 if (!$('#googleMapsApi').length) { 8732 // by default, jquery adds a cache-busting parameter on dynamically added script tags. set the ajaxSetup cache:true to prevent this 8733 $.ajaxSetup({ cache: true });
8734 var src = apiSrc + '&callback=Rock.controls.util.googleMapsIsLoaded'; 8735 $('head').prepend("<script id='googleMapsApi' src='" + src + "' />"); 8736 } else if (typeof google == 'object' && typeof google.maps == 'object') { 8737 this.googleMapsIsLoaded(); 8738 } 8739 }, 8740 // 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 8741 // 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 8742 getValueAsPixels: function (a) 8743 { 8744 var floatValue = parseFloat(a); 8745 if (floatValue) { 8746 return floatValue + 'px'; 8747 } 8748 else { 8749 return ''; 8750 } 8751 }, 8752 getScrollbarWidth: function () { 8753 // thx d.walsh 8754 var scrollDiv = document.createElement('div'); 8755 scrollDiv.className = 'modal-scrollbar-measure'; 8756 document.body.appendChild(scrollDiv); 8757 var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; 8758 document.body.removeChild(scrollDiv); 8759 return scrollbarWidth; 8760 } // Static 8761 }; 8762 8763 return exports; 8764 8765 }()); 8766}(jQuery)); 8767; 8768(function ($) { 8769 'use strict'; 8770 window.Rock = window.Rock || {}; 8771 Rock.controls = Rock.controls || {}; 8772 const DefaultComparisonTypes = [ 8773 { Value: 1, Text: 'Equal To' }, 8774 { Value: 2, Text: 'Not Equal To' }, 8775 { Value: 4, Text: 'Starts With' }, 8776 { Value: 8, Text: 'Contains' }, 8777 { Value: 16, Text: 'Does Not Contain' }, 8778 { Value: 32, Text: 'Is Blank' }, 8779 { Value: 64, Text: 'Is Not Blank' }, 8780 { Value: 2048, Text: 'Ends With' } 8781 ]; 8782 const GroupAll = 1; 8783 const GroupAny = 2; 8784 const GroupAllFalse = 3; 8785 const GroupAnyFalse = 4; 8786 8787 Rock.controls.valueFilter = (function () { 8788 // 8789 // Setup the UI so it reflects the initial data configured on the filter. 8790 // 8791 function loadInitialData($filter) { 8792 var data; 8793 8794 // 8795 // Try to parse the initial data. 8796 // 8797 try { 8798 data = JSON.parse($filter.find('input[type="hidden"]').val()); 8799 } 8800 catch (ex) { 8801 data = null; 8802 } 8803 8804 // 8805 // Initialize a new filter if we have no current settings. 8806 // 8807 if (data === null) { 8808 data = { ExpressionType: GroupAny, Filters: [] }; 8809 $filter.find('input[type="hidden"]').val(JSON.stringify(data)); 8810 } 8811 8812 // 8813 // Grab the objects we need from the filter control. 8814 // 8815 var $filterTypeAll = $filter.data('filterTypeAll'); 8816 var $filterTypeAny = $filter.data('filterTypeAny'); 8817 var options = $filter.data('options'); 8818 8819 // 8820 // Set the initial state of the Any/All toggle buttons. 8821 // 8822 if (data.ExpressionType === GroupAll) { 8823 $filterTypeAll.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass); 8824 $filterTypeAny.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass); 8825 } 8826 else { 8827 $filterTypeAny.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass); 8828 $filterTypeAll.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass); 8829 } 8830 8831 // 8832 // Add the initial field rows. 8833 // 8834 for (var index in data.Filters) { 8835 var expression = data.Filters[index]; 8836 8837 addFilterRow($filter, expression.Value, expression.Comparison); 8838 } 8839 8840 if (data.Filters.length === 0) { 8841 addFilterRow($filter, '', 8); 8842 updateData($filter); 8843 } 8844 } 8845 8846 // 8847 // Setup the initial event handlers for the controls in filter. 8848 // 8849 function addControlEvents($filter) { 8850 // 8851 // Load all the objects we need from the filter. 8852 // 8853 var $filterTypeAll = $filter.data('filterTypeAll'); 8854 var $filterTypeAny = $filter.data('filterTypeAny'); 8855 var options = $filter.data('options'); 8856 8857 // 8858 // Add a click handler on the "Any" toggle button that will turn off the 8859 // "All" option and turn on the "Any" option instead. 8860 // 8861 $filterTypeAny.on('click', function (e) { 8862 e.preventDefault(); 8863 8864 $filterTypeAny.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass); 8865 $filterTypeAll.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass); 8866 8867 updateData($filter); 8868 }); 8869 8870 // 8871 // Add a click handler on the "All" toggle button that will turn off the 8872 // "Any" option and turn on the "All" option instead. 8873 // 8874 $filterTypeAll.on('click', function (e) { 8875 e.preventDefault(); 8876 8877 $filterTypeAll.addClass(options.btnToggleOnClass).removeClass(options.btnToggleOffClass); 8878 $filterTypeAny.addClass(options.btnToggleOffClass).removeClass(options.btnToggleOnClass); 8879 8880 updateData($filter); 8881 }); 8882 8883 // 8884 // Add a click handler to the "Add" button that will add a new filter row. 8885 // 8886 $filter.data('addButton').on('click', function (e) { 8887 e.preventDefault(); 8888 addFilterRow($filter, '', 8); 8889 updateData($filter); 8890 }); 8891 } 8892 8893 // 8894 // Add a new field row. 8895 // 8896 function addFilterRow($filter, value, type) { 8897 var options = $filter.data('options'); 8898 var $comparisonButton = null; 8899 8900 // 8901 // Create the row container as an input-group so things look pretty. 8902 // 8903 var $row = $('<div class="input-group margin-b-sm"></div>'); 8904 8905 // 8906 // Create the text field the user can type into. 8907 // 8908 var $text = $('<input type="text" class="form-control">').appendTo($row); 8909 8910 // 8911 // Create the drop down that indicates what type of filter row this is. 8912 // 8913 var $inputGroupBtns = $('<span class="input-group-btn" />').appendTo($row); 8914 var $fieldTypeBtn = $('<button type="button" class="btn btn-default dropdown-toggle filter-dropdown-toggle" data-toggle="dropdown"><span /> <span class="caret" /></button>') 8915 .appendTo($inputGroupBtns); 8916 var $fieldTypeOptions = $('<ul class="dropdown-menu dropdown-menu-right"></ul>') 8917 .appendTo($inputGroupBtns); 8918 for (var i in options.comparisonTypes) { 8919 var $btn = $('<li><a href="#" data-value="' + options.comparisonTypes[i].Value + '">' + options.comparisonTypes[i].Text + '</a></li>').appendTo($fieldTypeOptions); 8920 if (type === options.comparisonTypes[i].Value) { 8921 $comparisonButton = $btn; 8922 } 8923 } 8924 $comparisonButton = $comparisonButton || $fieldTypeOptions.find('li:first-child'); 8925 8926 // 8927 // Create the delete button. 8928 // 8929 $('<button type="button" class="btn btn-danger btn-square"><i class="ti ti-x"></i></button>') 8930 .appendTo($inputGroupBtns); 8931 8932 // 8933 // Set default values. 8934 // 8935 $text.val(value); 8936 changeFilterRowType($filter, $comparisonButton, true); 8937 8938 // 8939 // Setup event handlers. 8940 // 8941 addFilterRowEvents($filter, $row); 8942 8943 // 8944 // Append this new filter row to the DOM. 8945 // 8946 $filter.data('rowContainer').append($row); 8947 } 8948 8949 // 8950 // Setup the event handlers for a newly created filter row. 8951 // 8952 function addFilterRowEvents($filter, $field) { 8953 // 8954 // When the text has changed, update the stored data. 8955 // 8956 $field.find('input[type="text"]').on('change', function () { 8957 updateData($filter); 8958 }); 8959 8960 // 8961 // When the user clicks on one of the filter type drop down options, update 8962 // the filter row type. 8963 // 8964 $field.find('ul > li > a').on('click', function (e) { 8965 e.preventDefault(); 8966 changeFilterRowType($filter, $(this)); 8967 }); 8968 8969 // 8970 // Remove a single row from the filter. 8971 // 8972 $field.find('button.btn-danger').on('click', function (e) { 8973 e.preventDefault(); 8974 $(this).closest('.input-group').remove(); 8975 updateData($filter); 8976 }); 8977 } 8978 8979 // 8980 // Update the filter row type based on what the user clicked. 8981 // 8982 function changeFilterRowType($filter, $btn, skipUpdate) { 8983 var $btnGroup = $btn.closest('.input-group-btn'); 8984 $btnGroup.find('.dropdown-toggle span:first-child').text($btn.text()); 8985 8986 if ($btn.data('value') === 32 || $btn.data('value') === 64) { 8987 $filter.find('input[type="text"]').val('').prop('disabled', true); 8988 } 8989 else { 8990 $filter.find('input[type="text"]').prop('disabled', false); 8991 } 8992 8993 if (skipUpdate !== true) { 8994 updateData($filter); 8995 } 8996 } 8997 8998 // 8999 // Update the hidden field to match what is in the UI. 9000 // 9001 function updateData($filter) { 9002 var options = $filter.data('options'); 9003 var filters = []; 9004 9005 // 9006 // Walk each row of the filter and build up the filters collection. 9007 // 9008 $filter.data('rowContainer').find('.input-group').each(function (index, element) { 9009 var $field = $(element); 9010 var text = $field.find('input[type="text"]').val(); 9011 var typeText = $field.find('button.dropdown-toggle span:first-child').text(); 9012 var type = $field.find('ul li a').filter(function () { return $(this).text() === typeText; }).data('value'); 9013 9014 filters.push({ Value: text, Comparison: type }); 9015 }); 9016 9017 // 9018 // If there is only one filter, it is a Contains filter and has no value, then get rid of it. 9019 // 9020 if (filters.length === 1 && filters[0].Value === '' && filters[0].Comparison === 8) { 9021 filters = []; 9022 } 9023 9024 // 9025 // Build the final filtered text value. 9026 // 9027 var data = { 9028 ExpressionType: $filter.data('filterTypeAny').hasClass(options.btnToggleOnClass) ? GroupAny : GroupAll, 9029 Filters: filters 9030 }; 9031 9032 $filter.find('input[type="hidden"]').val(JSON.stringify(data)); 9033 } 9034 9035 var exports = { 9036 initialize: function (options) { 9037 if (!options.controlId) { 9038 throw 'controlId is required'; 9039 } 9040 9041 // 9042 // Make sure we have minimum default options 9043 // 9044 options = $.extend({ 9045 btnToggleOnClass: 'btn-info', 9046 btnToggleOffClass: 'btn-default', 9047 required: true, 9048 requiredMessage: 'The field is required', 9049 hideFilterMode: false, 9050 comparisonTypes: DefaultComparisonTypes, 9051 defaultComparison: 8 9052 }, options); 9053 9054 // 9055 // Setup the comparsionTypesByValue object. 9056 // 9057 options.comparisonTypeByValue = {}; 9058 for (var i = 0; i < options.comparisonTypes.length; i++) { 9059 options.comparisonTypeByValue[options.comparisonTypes[i].Value] = options.comparisonTypes[i].Text; 9060 } 9061 9062 // 9063 // Find the filter control placeholder. 9064 // 9065 var $filter = $('#' + options.controlId); 9066 9067 // 9068 // Setup the "Any/All" buttons for the filter. 9069 //
9070 var $filterTypeContainer = $('<div class="text-right" />').appendTo($filter); 9071 var $filterTypeGroup = $('<div class="btn-group" role="group" />').appendTo($filterTypeContainer); 9072 var $filterTypeAny = $('<button type="button" class="btn btn-default btn-xs">Any</button>').appendTo($filterTypeGroup); 9073 var $filterTypeAll = $('<button type="button" class="btn btn-default btn-xs">All</button>').appendTo($filterTypeGroup); 9074 if (options.hideFilterMode === true) { 9075 $filterTypeContainer.addClass('hidden'); 9076 } 9077 9078 // 9079 // Setup the placeholder div that will contain all the filter rows. 9080 // 9081 var $rowContainer = $('<div />').appendTo($filter); 9082 9083 // 9084 // Setup the add button. 9085 // 9086 var $addButtonContainer = $('<div class="text-right" />').appendTo($filter); 9087 var $addButton = $('<a href="#" class="btn btn-default btn-square btn-sm"><i class="ti ti-plus"></i></a>').appendTo($addButtonContainer); 9088 9089 // 9090 // Store all the objects we need access to later as jQuery data objects 9091 // on the filter control. 9092 // 9093 $filter.data('options', options); 9094 $filter.data('filterTypeAny', $filterTypeAny); 9095 $filter.data('filterTypeAll', $filterTypeAll); 9096 $filter.data('rowContainer', $rowContainer); 9097 $filter.data('addButton', $addButton); 9098 9099 // 9100 // Setup initial control events. 9101 // 9102 addControlEvents($filter); 9103 9104 // 9105 // Setup UI to reflect initial data. 9106 // 9107 loadInitialData($filter); 9108 }, 9109 clientValidate: function (validator, args) { 9110 var $filter = $(validator).prev(); 9111 var required = $filter.data('options').required; 9112 var data = JSON.parse($filter.find('input[type="hidden"]').val()); 9113 9114 var isValid = !required || data.Filters.length > 0; 9115 9116 if (isValid) { 9117 $filter.closest('.form-group').removeClass('has-error'); 9118 args.IsValid = true; 9119 } 9120 else { 9121 $filter.closest('.form-group').addClass('has-error'); 9122 args.IsValid = false; 9123 validator.errormessage = $filter.data('options').requiredMessage; 9124 } 9125 } 9126 }; 9127 9128 return exports; 9129 }()); 9130}(jQuery)); 9131; 9132; (function () { 9133 function updateKeyValues(e) { 9134 var $span = e.closest('span.value-list'); 9135 var newValue = ''; 9136 9137 var valueDelimiters = ['|', ',']; 9138 $span.children('span.value-list-rows').first().children('div.controls-row').each(function (index) { 9139 var value = $(this).children('.js-value-list-input').first().val(); 9140 9141 valueDelimiters.forEach(function (v, i, a) { 9142 var re = new RegExp('\\' + v, 'g'); 9143 if (value.indexOf(v) > -1) { 9144 value = value.replace(re, encodeURIComponent(v)); 9145 } 9146 }); 9147 newValue += value + '|' 9148 }); 9149 9150 $span.children('input').first().val(newValue); 9151 } 9152 9153 Sys.Application.add_load(function () { 9154 9155 $('a.value-list-add').on('click', function (e) { 9156 e.preventDefault(); 9157 var $ValueList = $(this).closest('.value-list'); 9158 var newValuePickerHtml = $ValueList.find('.js-value-list-html').val() 9159 $ValueList.find('.value-list-rows').append(newValuePickerHtml); 9160 updateKeyValues($(this)); 9161 Rock.controls.modal.updateSize($(this)); 9162 }); 9163 9164 $(document).on('click', 'a.value-list-remove', function (e) { 9165 e.preventDefault(); 9166 var $rows = $(this).closest('span.value-list-rows'); 9167 $(this).closest('div.controls-row').remove(); 9168 updateKeyValues($rows); 9169 Rock.controls.modal.updateSize($(this)); 9170 });
9171 9172 $(document).on('change', '.js-value-list-input', function (e) { 9173 updateKeyValues($(this)); 9174 }); 9175 }); 9176})(); 9177; 9178(function (Sys) { 9179 'use strict'; 9180 Sys.Application.add_load(function () { 9181 $('a.warning').on('click', function (e) { 9182 e.preventDefault(); 9183 $(this).siblings('div.alert-warning').slideToggle(function () { 9184 Rock.controls.modal.updateSize(this); 9185 }); 9186 $(this).siblings('div.alert-info').slideUp(); 9187 }); 9188 }); 9189}(Sys)); 9190; 9191/* NOTE: Requires this on the init of the block 9192 9193RockPage.AddScriptLink("~/Scripts/d3-cloud/d3.layout.cloud.js"); 9194RockPage.AddScriptLink("~/Scripts/d3-cloud/d3.min.js"); 9195 9196*/ 9197 9198(function ($) 9199{ 9200 'use strict'; 9201 window.Rock = window.Rock || {}; 9202 Rock.controls = Rock.controls || {}; 9203 9204 Rock.controls.wordcloud = (function () 9205 { 9206 9207 var exports = { 9208 initialize: function (options) 9209 { 9210 if (!options.inputTextId) { 9211 throw 'inputTextId is required'; 9212 } 9213 9214 if (!options.visId) { 9215 throw 'visId is required'; 9216 } 9217 9218 // if the visId element isn't found, jump out 9219 if (d3.select("#" + options.visId).empty()) { 9220 return; 9221 } 9222 9223 var $inputText = $('#' + options.inputTextId); 9224 var visId = options.visId; 9225 var statusId = options.statusId; 9226 var svgWidth = options.width || 800; 9227 var svgHeight = options.height || 300; 9228 var fontName = options.fontName || 'Impact'; 9229 var maxWords = options.maxWords || 255; 9230 var scaleName = options.scaleName || 'log'; 9231 var spiralName = options.spiralName || 'archimedean'; 9232 var colors = options.colors || ['#0193B9', '#F2C852', '#1DB82B', '#2B515D', '#ED3223']; 9233 9234 var angleCount = options.angleCount == undefined ? 6 : options.angleCount; 9235 if (angleCount > 180) { 9236 angleCount = 180; 9237 } 9238 9239 // a good max range is -90 to 90 so that words don't go upside down 9240 var angleMin = options.angleMin == undefined ? -90 : options.angleMin; 9241 var angleMax = options.angleMax == undefined ? 90 : options.angleMax; 9242 var angles = []; 9243 if (angleCount == 1) { 9244 // one angle, so choose the midpoint of the angleMin and angleMax 9245 angles.push((angleMax + angleMin) / 2); 9246 } 9247 else if (angleCount == 2) { 9248 // two angles, so choose the angleMin and angleMax 9249 angles.push(angleMin); 9250 angles.push(angleMax); 9251 } 9252 else if (angleCount > 2) { 9253 // three or more, so choose the angleMin and angleMax, and the the remaining ones between 9254 var angleSize = (angleMax - angleMin) / (angleCount - 1) 9255 var angle = angleMin; 9256 while (angle <= angleMax) { 9257 angles.push(angle); 9258 angle += angleSize; 9259 } 9260 } 9261 9262 /* from cloud.js */ 9263 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", 9264 fill = d3.scaleLinear() 9265 .domain([0, 1, 2, 3, 4, 5, 6, 10, 15, 20, 100]) 9266 .range(d3.schemeCategory20b), 9267 w = svgWidth, 9268 h = svgHeight, 9269 words = [], 9270 max, scale = 1, 9271 complete = 0, 9272 keyword = "", 9273 tags, fontSize, maxLength = 30, 9274 fetcher, statusText = d3.select("#" + statusId), 9275 layout = d3.layout.cloud().timeInterval(10).size([w, h]).fontSize(function (t) 9276 { 9277 return fontSize(+t.value) 9278 }).text(function (t) 9279 { 9280 return t.key 9281 }).on("word", progress).on("end", draw), 9282 svg = d3.select("#" + visId).append("svg").attr("width", w).attr("height", h), 9283 background = svg.append("g"), 9284 vis = svg.append("g").attr("transform", "translate(" + [w >> 1, h >> 1] + ")"); 9285 9286 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)$/, 9287 punctuation = new RegExp("[" + unicodePunctuationRe + "]", "g"), 9288 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, 9289 discard = /^(@|https?:|\/\/)/, 9290 htmlTags = /(<[^>]*?>|<script.*?<\/script>|<style.*?<\/style>|<head.*?><\/head>)/g, 9291 matchTwitter = /^https?:\/\/([^\.]*\.)?twitter\.com/; 9292 9293 var inputText = $inputText.val(); 9294 if (inputText) { 9295 parseText(inputText); 9296 } 9297 9298 function flatten(t, e) 9299 { 9300 if ("string" == typeof t) return t; 9301 var n = []; 9302 for (e in t) { 9303 var a = flatten(t[e], e); 9304 a && n.push(a) 9305 } 9306 return n.join(" ") 9307 } 9308 9309 function parseText(t) 9310 { 9311 tags = {}; 9312 var e = {}, 9313 n = false//d3.select("#per-line").property("checked");
9314 t.split(n ? /\n/g : wordSeparators).forEach(function (t) 9315 { 9316 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)) 9317 }), tags = d3.entries(tags).sort(function (t, e) 9318 { 9319 return e.value - t.value 9320 }), tags.forEach(function (t) 9321 { 9322 t.key = e[t.key] 9323 }), generate() 9324 } 9325 9326 function generate() 9327 { 9328 layout 9329 .font(fontName) 9330 .spiral(spiralName) 9331 .rotate(function () 9332 { 9333 return angles[Math.floor(Math.random() * angles.length)]; 9334 }), 9335 fontSize = d3.scaleLog().range([10, 100]), tags.length && fontSize.domain([+tags[tags.length - 1].value || 1, +tags[0].value]), 9336 complete = 0, statusText.style("display", null), 9337 words = [], 9338 layout.stop().words(tags.slice(0, max = Math.min(tags.length, +maxWords))).start() 9339 } 9340 9341 function progress(t) 9342 { 9343 statusText.text(++complete + "/" + max) 9344 } 9345 9346 function draw(t, e) 9347 { 9348 statusText.style("display", "none"), 9349 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; 9350 var n = vis.selectAll("text").data(words, function (t) 9351 { 9352 return t.text.toLowerCase() 9353 }); 9354 n.transition().duration(1e3).attr("transform", function (t) 9355 { 9356 return "translate(" + [t.x, t.y] + ")rotate(" + t.rotate + ")" 9357 }).style("font-size", function (t) 9358 { 9359 return t.size + "px" 9360 }), 9361 9362 n.enter().append("text").attr("text-anchor", "middle").attr("transform", function (t) 9363 { 9364 return "translate(" + [t.x, t.y] + ")rotate(" + t.rotate + ")" 9365 }).style("font-size", "1px").transition().duration(1e3).style("font-size", function (t) 9366 { 9367 return t.size + "px" 9368 }).style("font-family", function (t) 9369 { 9370 return t.font 9371 }).text(function (t) 9372 { 9373 return t.text 9374 }).style("fill", function (t) 9375 { 9376 return colors[Math.floor(Math.random() * colors.length)]; 9377 }); 9378 9379 var a = background.append("g").attr("transform", vis.attr("transform")), 9380 r = a.node(); 9381 n.exit().each(function () 9382 { 9383 r.appendChild(this) 9384 }), a.transition().duration(1e3).style("opacity", 1e-6).remove(), vis.transition().delay(1e3).duration(750).attr("transform", "translate(" + [w >> 1, h >> 1] + ")scale(" + scale + ")") 9385 } 9386 9387 } 9388 } 9389 9390 return exports; 9391 }()); 9392}(jQuery)); 9393; 9394(function ($) { 9395 'use strict'; 9396 window.Rock = window.Rock || {}; 9397 Rock.controls = Rock.controls || {}; 9398 9399 Rock.controls.yearPicker = (function () { 9400 var exports = { 9401 initialize: function (options) { 9402 if (!options.id) { 9403 throw 'id is required'; 9404 } 9405 9406 var earliestDate = new Date(1000, 1, 1); 9407 9408 // uses https://github.com/eternicode/bootstrap-datepicker 9409 $('#' + options.id).datepicker({ 9410 format: 'yyyy', 9411 assumeNearbyYear: 10, 9412 autoclose: true, 9413 startView: 2, 9414 minViewMode: 2, 9415 startDate: earliestDate, 9416 zIndexOffset: 1050 9417 }); 9418 9419 } 9420 }; 9421 9422 return exports; 9423 }()); 9424}(jQuery)); 9425; 9426var loadFunction = function () { 9427 // handle what should happen when a different compare type is selected 9428 function updateFilterControls(filterCompareControl) { 9429 var $fieldCriteriaRow = $(filterCompareControl).closest('.field-criteria'); 9430 var compareValue = $(filterCompareControl).val(); 9431 var isNullCompare = (compareValue == 32 || compareValue == 64); 9432 var isBetweenCompare = (compareValue == 4096); 9433 if (isNullCompare) { 9434 $fieldCriteriaRow.find('.js-filter-control').hide(); 9435 $fieldCriteriaRow.find('.js-filter-control-between').hide(); 9436 } 9437 else if (isBetweenCompare) { 9438 $fieldCriteriaRow.find('.js-filter-control').hide(); 9439 $fieldCriteriaRow.find('.js-filter-control-between').show(); 9440 } 9441 else { 9442 $fieldCriteriaRow.find('.js-filter-control').show(); 9443 $fieldCriteriaRow.find('.js-filter-control-between').hide(); 9444 } 9445 } 9446 9447 $('.js-filter-compare').each(function (e) { 9448 updateFilterControls(this); 9449 });
9450 9451 $('.js-filter-compare').on("change", function () { 9452 updateFilterControls(this); 9453 }); 9454 9455 // handle property selection changes from the EntityFieldFilter 9456 $('select.entity-property-selection').on("change", function () { 9457 var $parentRow = $(this).closest('.js-filter-row'); 9458 $parentRow.find('div.field-criteria').hide(); 9459 $parentRow.find('div.field-criteria').eq($(this).find(':selected').index()).show(); 9460 }); 9461 9462 // activity animation on filter field cootrol 9463 $('.filter-item > header').on("click", function () { 9464 $(this).siblings('.panel-body').slideToggle(); 9465 $(this).children('div.pull-left').children('div').slideToggle(); 9466 9467 $expanded = $(this).children('input.filter-expanded'); 9468 $expanded.val($expanded.val() == 'True' ? 'False' : 'True'); 9469 9470 $('a.filter-view-state > i', this).toggleClass('ti-chevron-down'); 9471 $('a.filter-view-state > i', this).toggleClass('ti-chevron-up'); 9472 }); 9473 9474 // fix so that the Remove button will fire its event, but not the parent event 9475 $('.filter-item a.btn-danger').on("click", function (event) { 9476 event.stopImmediatePropagation(); 9477 }); 9478 9479 $('.filter-item-select').on("click", function (event) { 9480 event.stopImmediatePropagation(); 9481 }); 9482} 9483 9484$(document).ready(function () { 9485 loadFunction(); 9486 Sys.Application.add_load(loadFunction); 9487}); 9488 9489// 9490(function ($) { 9491 'use strict'; 9492 window.Rock = window.Rock || {}; 9493 9494 Rock.reporting = (function () { 9495 var _reporting = {}, 9496 exports = { 9497 // 9498 formatFilterForDateField: function (title, $selectedContent) { 9499 var betweenMode = $('.js-filter-control-between', $selectedContent).is(':visible'); 9500 var dateValue = ''; 9501 if (betweenMode) { 9502 return title + ' during ' + $('.js-slidingdaterange-text-value', $selectedContent).val(); 9503 } else { 9504 var useCurrentDateOffset = $('.js-current-date-checkbox', $selectedContent).is(':checked'); 9505 9506 if (useCurrentDateOffset) { 9507 var daysOffset = $('.js-current-date-offset', $selectedContent).val(); 9508 if (daysOffset > 0) { 9509 dateValue = 'Current Date plus ' + daysOffset + ' days'; 9510 } 9511 else if (daysOffset < 0) { 9512 dateValue = 'Current Date minus ' + -daysOffset + ' days'; 9513 } 9514 else { 9515 dateValue = 'Current Date'; 9516 } 9517 } 9518 else { 9519 dateValue = $('.js-date-picker input', $selectedContent).filter(':visible').val(); 9520 } 9521 return title + ' ' + $('.js-filter-compare', $selectedContent).find(':selected').text() + ' \'' + dateValue + '\'' 9522 } 9523 }, 9524 9525 // 9526 formatFilterForDateTimeField: function (title, $selectedContent) { 9527 var betweenMode = $('.js-filter-control-between', $selectedContent).is(':visible'); 9528 var dateValue = ''; 9529 var timeValue = ''; 9530 if (betweenMode) { 9531 return title + ' during ' + $('.js-slidingdaterange-text-value', $selectedContent).val(); 9532 } else { 9533 var useCurrentDateOffset = $('.js-current-datetime-checkbox', $selectedContent).is(':checked'); 9534 9535 if (useCurrentDateOffset) { 9536 var minutesOffset = Number($('.js-current-datetime-offset', $selectedContent).val()); 9537 if (minutesOffset > 0) { 9538 dateValue = 'Current Time plus ' + minutesOffset + ' minutes'; 9539 } 9540 else if (minutesOffset < 0) { 9541 dateValue = 'Current Time minus ' + -minutesOffset + ' minutes'; 9542 } 9543 else { 9544 dateValue = 'Current Time'; 9545 } 9546 } 9547 else { 9548 dateValue = $('input.js-datetime-date', $selectedContent).filter(':visible').val() || ''; 9549 timeValue = $('input.js-datetime-time', $selectedContent).filter(':visible').val() || ''; 9550 } 9551 9552 var dateTimeValue = (dateValue + ' ' + timeValue).trim(); 9553 return title + ' ' + $('.js-filter-compare', $selectedContent).find(':selected').text() + ' \'' + dateTimeValue + '\'' 9554 } 9555 }, 9556 9557 // 9558 formatFilterForCheckBoxListFilterControl: function (title, $selectedContent) { 9559 var selectedItems = ''; 9560 $('input:checked', $selectedContent).each( 9561 function () { 9562 selectedItems += selectedItems == '' ? '' : ' OR '; 9563 selectedItems += ' \'' + $(this).parent().text() + '\''; 9564 }); 9565 9566 return title + ' is ' + selectedItems 9567 }, 9568 9569 // 9570 formatFilterForDefinedValueField: function (title, $selectedContent) { 9571 return formatFilterForCheckBoxListFilterControl(title, $selectedContent); 9572 }, 9573 9574 // NOTE: this is specifically for the Rock.Reporting.DataFilter.OtherDataViewFilter (and similar) components 9575 formatFilterForOtherDataViewFilter: function (title, $selectedContent) { 9576 var dataViewName = $('.js-dataview .js-item-name-value', $selectedContent).val(); 9577 return title + ' ' + dataViewName; 9578 }, 9579 9580 // 9581 formatFilterForSelectSingleField: function (title, $selectedContent) { 9582 return formatFilterForCheckBoxListFilterControl(title, $selectedContent); 9583 }, 9584 9585 // NOTE: this is specifically for the Rock.Reporting.DataFilter.Person.InGroupFilter component 9586 formatFilterForGroupFilterField: function (title, $selectedContent) { 9587 var groupNames = $('.js-group-picker', $selectedContent).find('.selected-names').text(); 9588 var checkedRoles = $('.js-roles', $selectedContent).find(':checked').closest('label'); 9589 var result = title + ' ' + groupNames; 9590 var includeChildGroups = $('.js-include-child-groups', $selectedContent).is(':checked'); 9591 if (includeChildGroups) { 9592 9593 var includeDescendantGroups = $('.js-include-child-groups-descendants', $selectedContent).is(':checked'); 9594 var includeSelectedGroups = $('.js-include-selected-groups', $selectedContent).is(':checked'); 9595 var includeInactiveGroups = $('.js-include-inactive-groups', $selectedContent).is(':checked'); 9596 if (includeDescendantGroups) { 9597 result = result + ' OR descendant groups'; 9598 } else { 9599 result = result + ' OR child groups'; 9600 } 9601 9602 if (includeInactiveGroups) { 9603 result += ", including inactive groups"; 9604 } 9605 9606 if (!includeSelectedGroups) { 9607 result = result + ', NOT including selected groups'; 9608 } 9609 } 9610 9611 if (checkedRoles.length > 0) { 9612 var roleCommaList = checkedRoles.map(function () { { return $(this).text() } }).get().join(','); 9613 result = result + ', with role(s): ' + roleCommaList; 9614 } 9615 9616 var groupMemberStatus = $('.js-group-member-status option:selected', $selectedContent).text(); 9617 if (groupMemberStatus) { 9618 result = result + ', with member status:' + groupMemberStatus; 9619 } 9620 9621 var dateAddedDateRangeText = $('.js-dateadded-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val() 9622 if (dateAddedDateRangeText) { 9623 result = result + ', added to group in Date Range: ' + dateAddedDateRangeText; 9624 } 9625 9626 var firstAttendanceDateRangeText = $('.js-firstattendance-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val() 9627 if (firstAttendanceDateRangeText) { 9628 result = result + ', first attendance to group in Date Range: ' + firstAttendanceDateRangeText; 9629 } 9630 9631 var lastAttendanceDateRangeText = $('.js-lastattendance-sliding-date-range .js-slidingdaterange-text-value', $selectedContent).val() 9632 if (lastAttendanceDateRangeText) { 9633 result = result + ', last attendance to group in Date Range: ' + lastAttendanceDateRangeText; 9634 } 9635 9636 return result; 9637 }, 9638 9639 // NOTE: this is specifically for the Rock.Reporting.DataFilter.Person.HasPhoneFilter component 9640 formatFilterForHasPhoneFilter: function ($content) { 9641 9642 var has; 9643 if ($('.js-hasphoneoftype', $content).find(':selected').val() == "True") { 9644 has = "Has "; 9645 } else { 9646 has = "Doesn't have "; 9647 } 9648 9649 var phoneType = $('.js-phonetype', $content).find(':selected').text(); 9650 var sms = $('.js-hassms', $content).find(':selected').text(); 9651 9652 if (sms == 'Yes') { 9653 sms = ' AND has SMS Enabled'; 9654 } 9655 else if (sms == 'No') { 9656 sms = " AND doesn't have SMS Enabled"; 9657 } 9658 9659 var result = has + phoneType + sms; 9660 9661 return result; 9662 }, 9663 9664 // 9665 formatFilterDefault: function (title, $selectedContent) { 9666 var compareTypeText = $('.js-filter-compare', $selectedContent).find(':selected').text(); 9667 9668 var compareValueText = $('input[type=text].js-filter-control', $selectedContent).val(); // textbox value. 9669 if (!compareValueText || compareValueText == "") { 9670 compareValueText = $('.js-filter-control', $selectedContent).find(':selected').map(function () { return this.text; }).get().join("', '"); 9671 } 9672 if (!compareValueText || compareValueText == "") { 9673 compareValueText = $('.js-filter-control', $selectedContent).find(':checked').next().map(function () { return $(this).text(); }).get().join("', '"); 9674 } 9675 9676 var result = title; 9677 if ($('.js-filter-control', $selectedContent).is(':visible')) { 9678 result = title + ' ' + compareTypeText + " '" + compareValueText + "'"; 9679 } else { 9680 result = title + ' ' + compareTypeText; 9681 } 9682 9683 return result; 9684 } 9685 }; 9686 9687 return exports; 9688 }()); 9689}(jQuery)); 9690;
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.