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