1(function($, SystemJS, _) { 2 3// Depends on php.js for http_build_query 4 5// Prevent re-initialization 6if (typeof window.Shout != 'undefined') 7{ 8 if (typeof window.ShoutConfiguration != 'undefined') 9 { 10 window.Shout.configuration = window.ShoutConfiguration.configuration; 11 } 12 13 return; 14} 15// From jQuery 16// A simple way to check for HTML strings or ID strings 17// (both of which we optimize for) 18var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/, 19 20// Is it a simple selector 21 isSimple = /^.[^:#\[\.]*$/, 22 23// IsPHPArray 24 phpVariable = /([^\[\]]*)(\[.*\])*/; 25 26var objectCache = []; 27var elementCache = []; 28 29var _maxZ = 1000; 30var _nextId = 0; 31var dropArray = {}; 32 33var Shout = window.Shout = function(selector) 34{ 35 return new Shout.fn.init(selector); 36}; 37 38if(window.console === undefined) 39{ 40 window.console = { 41 log:function(){ 42 return true; 43 }, 44 debug:function(){ 45 return true; 46 }, 47 info:function(){ 48 return true; 49 } 50 }; 51} 52 53Shout.fn = Shout.prototype = { 54 nextId: function() 55 { 56 return 'Shout-Gen-' + _nextId++; 57 }, 58 init: function(selector) { 59 60 if(!selector) 61 { 62 return this; 63 } 64 else if(selector && selector.nodeEl) 65 { 66 return selector; 67 } 68 else if ( typeof selector == "string" ) 69 { 70 // Use jquery expression to determine if we've got 71 // and id string. 72 73 var match = quickExpr.exec( selector ); 74 75 if(match && match[3] !== undefined) 76 { 77 if(objectCache[match[3]]) 78 { 79 return objectCache[match[3]]; 80 } 81 else 82 { 83 objectCache[match[3]] = this; 84 } 85 } 86 this.nodeEl = $(selector); 87 } 88 else if(selector) 89 { 90 this.nodeEl = $(selector); 91 } 92 else 93 { 94 95 } 96 97 this.attachParams = null; 98 this.inherittedMove = false; 99 this.shoutId = ''; 100 this.attachments = []; 101 this.constraints = {}; 102 103 this.dragProxy = null; 104 this.dropProxy = null; 105 106 this.shoutId = this.nextId(); 107 108 this.nodeEl.data('shoutObj',this); 109 110 if (typeof window.ShoutConfiguration != 'undefined') 111 { 112 this.configuration = window.ShoutConfiguration.configuration; 113 } 114 115 return this; 116 117 } 118}; 119 120// Give the init function the Shout prototype 121 122Shout.fn.init.prototype = Shout.fn; 123 124function _tooltipNormalizeConfig(config) 125{ 126 var default_driver; 127 128 if (window.tippy) 129 { 130 default_driver = 'tippy'; 131 } 132 else if ($.fn.qtip) 133 { 134 default_driver = 'qtip'; 135 } 136 else 137 { 138 throw 'no valid tooltip drivers'; 139 } 140 141 var default_config = { 142 driver: default_driver, 143 config: 'sui-tooltip-light', 144 selector_mode: 'jquery', 145 //please limit the use of the driver_config, since it causes us to have less flexibility in changing between tooltip drivers 146 driver_config: { 147 tippy: { 148 }, 149 qtip: { 150 }, 151 } 152 }; 153 154 if (typeof config === 'string') 155 { 156 config = $.extend(default_config, {config: config}); 157 } 158 else if (typeof config === 'object') 159 { 160 config = $.extend(default_config, config); 161 } 162 else 163 { 164 config = default_config; 165 } 166 167 var valid_drivers = ['qtip', 'tippy']; 168 if (typeof config.driver !== 'string' || -1 === _.indexOf(valid_drivers, config.driver)) 169 { 170 throw 'invalid tooltip driver. Should be one of: '+JSON.stringify(valid_drivers); 171 } 172 173 var valid_configs = ['', 'sui-tooltip-light', 'sui-tooltip-dark', 'sui-sf-tooltip-light', 'sui-sf-tooltip-dark', 'sui-tooltip-interactive', 'sui-tooltip-has_arrow']; 174 if (typeof config.config !== 'string' || _.difference(_.split(config.config, ' '), valid_configs).length > 0) 175 { 176 throw 'invalid tooltip config. Should be combination of: '+JSON.stringify(valid_configs); 177 } 178 179 return config; 180} 181 182// private to Shout obj 183function _tooltip(el, content, config) 184{ 185 $el = $(el); 186 187 config = _tooltipNormalizeConfig(config); 188 189 if ('qtip' === config.driver && $.fn.qtip) 190 { 191 var args = {}; 192 var default_args = { 193 content: { 194 'text' : '' 195 }, 196 position:{ 197 corner: { 198 target: 'topRight', 199 tooltip: 'bottomLeft' 200 }, 201 adjust: { 202 'screen':true 203 } 204 }, 205 show: { 206 delay: 0 207 }, 208 hide: { 209 delay: 200, 210 fixed: true 211 }, 212 style : { 213 classes: 'qtip-light', 214 width:300, 215 tip: false 216 } 217 }; 218 219 switch (true) 220 { 221 case 'sui-tooltip-dark' === config.config: 222 var config_args = { 223 style: { 224 classes: 'qtip-dark', 225 } 226 }; 227 default_args = $.extend(default_args, config_args); 228 break; 229 230 case 'sui-sf-tooltip-light' === config.config: 231 break; 232 233 case 'sui-sf-tooltip-dark' === config.config: 234 break; 235 236 case 'sui-tooltip-light' === config.config: 237 break; 238 239 case 'sui-tooltip-has_arrow' === config.config: 240 break; 241 242 default: 243 throw 'unknown tooltip config'; 244 } 245 246 if (typeof content === 'object') 247 { 248 args = content; 249 } 250 else if (typeof content === 'string' || typeof content === 'function') 251 { 252 args = { 253 content: { 254 'text' : content 255 } 256 }; 257 } 258 else 259 { 260 throw 'unable to determine tooltip config'; 261 } 262 263 args = $.extend(default_args, args); 264 265 $el.qtip(args); 266 267 return; 268 } 269 else if ('tippy' === config.driver && window.tippy) 270 { 271 var $tippyroot = $('> .sui-tippy', document.body); 272 273 if (0 === $tippyroot.length) 274 { 275 $tippyroot = $('<div class="sui-tippy"></div>'); 276 $(document.body).append($tippyroot); 277 } 278 279 var args; 280 var default_args = { 281 content: content, 282 duration: 0, 283 theme: 'light sui-tippyshout', 284 // placement: 'top-start', 285 appendTo: $tippyroot.get(0),
286 arrow: false, 287 allowHTML: true 288 }; 289 290 config_strings = _.split(config.config, ' '); 291 292 _.forEach(config_strings, function(config_string) { 293 294 switch (true) 295 { 296 case 'sui-tooltip-light' === config_string: 297 break; 298 299 case 'sui-tooltip-dark' === config_string: 300 var config_args = { 301 theme: 'dark sui-tippyshout' 302 }; 303 default_args = $.extend(default_args, config_args); 304 break; 305 306 case 'sui-sf-tooltip-dark' === config_string: 307 var config_args = { 308 theme: 'sui-sf-tooltip dark sui-sf-tooltip-dark sui-sf-tooltip-leftaligned' 309 }; 310 default_args = $.extend(default_args, config_args); 311 break; 312 313 case 'sui-sf-tooltip-light' === config_string: 314 var config_args = { 315 theme: 'sui-sf-tooltip light sui-sf-tooltip-light sui-sf-tooltip-leftaligned' 316 }; 317 default_args = $.extend(default_args, config_args); 318 break; 319 320 case 'sui-tooltip-interactive' === config_string: 321 var config_args = { 322 interactive: true 323 }; 324 default_args = $.extend(default_args, config_args); 325 break; 326 327 case 'sui-tooltip-has_arrow' === config_string: 328 var config_args = { 329 arrow: true 330 }; 331 default_args = $.extend(default_args, config_args); 332 break; 333 334 default: 335 throw 'unknown tooltip config'; 336 } 337 338 }); 339 340 if (config.driver_config && config.driver_config[config.driver] && Object.keys(config.driver_config[config.driver]).length) 341 { 342 args = $.extend(default_args, config.driver_config[config.driver]); 343 } 344 345 if (false && typeof result === 'object' && typeof result.then === 'function') 346 { 347 throw 'TODO: promise implementation'; 348 } 349 else if (typeof content === 'object') 350 { 351 args = content; 352 } 353 else if (typeof content === 'string') 354 { 355 args = { 356 content: content 357 }; 358 } 359 else if (typeof content === 'function') 360 { 361 args = { 362 content: content 363 }; 364 } 365 else 366 { 367 throw 'unable to determine tooltip config'; 368 } 369 370 args = $.extend(default_args, args); 371 372 if (config.delegate) 373 { 374 if ('native' === config.selector_mode) 375 { 376 args.target = el; 377 console.log(el, config, args); 378 var x = tippy.delegate(config.delegate, args); 379 console.log(x); 380 return; 381 } 382/* 383 if ('jquery' === config.selector_mode) 384 { 385 $el.each( 386 function () 387 { 388 tippy(this, args); 389 } 390 ); 391 return; 392 } 393*/ 394 throw 'invalid tooltip selector mode. Should be [native, jquery, ]'; 395 } 396 397 if ('native' === config.selector_mode) 398 { 399 tippy(el, args); 400 return; 401 } 402 403 if ('jquery' === config.selector_mode) 404 { 405 $el.each( 406 function () 407 { 408 tippy(this, args); 409 } 410 ); 411 return; 412 } 413 414 throw 'invalid tooltip selector mode. Should be [native, jquery, ]'; 415 } 416 else 417 { 418 throw 'invalid driver specified'; 419 } 420 421 throw 'unable to create tooltip'; 422}; 423 424function _tooltipify() 425{ 426 tippy.delegate('body', { 427 target: 'abbr.sui-abbr-tooltip', 428 content: function (reference) { 429 const title = reference.getAttribute('title'); 430 reference.removeAttribute('title'); 431 return title; 432 }, 433 appendTo: document.body, 434 }); 435} 436 437// https://javascript.info/promise-chaining 438// https://davidwalsh.name/javascript-loader 439// https://stackoverflow.com/questions/14644558/call-javascript-function-after-script-is-loaded 440var load = (function() { 441 // Function which returns a function: https://davidwalsh.name/javascript-functions 442 function _load(tag) { 443 return function(url) { 444 // This promise will be used by Promise.all to determine success or failure 445 return new Promise(function(resolve, reject) { 446 var element = document.createElement(tag); 447 var parent = 'body'; 448 449 var attr = 'src'; 450 451 // Important success and error for the promise 452 element.onload = function() { 453 resolve(url); 454 }; 455 element.onerror = function() { 456 reject(url); 457 }; 458 459 // Need to set different attributes depending on tag type 460 switch(tag) { 461 case 'script': 462 element.async = true; 463 break; 464 465 case 'link': 466 element.type = 'text/css'; 467 element.rel = 'stylesheet'; 468 attr = 'href'; 469 parent = 'head'; 470 } 471 472 // Inject into document to kick off loading 473 element[attr] = url; 474 document[parent].appendChild(element); 475 }); 476 }; 477 } 478 479 return { 480 css: _load('link'), 481 js: _load('script'), 482 img: _load('img') 483 } 484})(); 485 486$.extend(Shout, { 487 formmodified: false, 488 489 scriptsloaded: [], 490 491// configuration: {}, 492 configuration: ((typeof window.ShoutConfiguration != 'undefined') ? window.ShoutConfiguration.configuration : {}), 493 494 configure: function(params) 495 { 496 if (typeof window.ShoutConfiguration != 'undefined') 497 { 498 window.ShoutConfiguration.configure(params); 499 } 500 501 $.extend(true, this.configuration, params); 502 }, 503 504 objSerialize: function(fields) 505 { 506 var returnObj = {}; 507 508 $.each($(fields),function() 509 { 510 var field = $(this); 511 returnObj[field.attr('name')] = field.val(); 512 }); 513 514 return returnObj; 515 }, 516 517 loadFields: function(region,dataArray,prefix) 518 { 519 var params = []; 520 521 if(dataArray) 522 { 523 $.each(dataArray, function(e,obj) { 524 $.each(obj, function(j, data) 525 { 526 var match = phpVariable.exec(j); 527 if(match[2]) 528 { 529 var str = '['+e+']'; 530 531 if(match[1]) 532 { 533 str += '['+match[1]+']'; 534 } 535 536 str += ''+match[2]+'='+data; 537 538 params.push(str); 539 } 540 else 541 { 542 params.push('['+e+']['+j+']='+data); 543 } 544 }); 545 }); 546 } 547 548 $.each(params,function(i) 549 { 550 551 region.find('[name='+prefix+i+']').val(this); 552 }); 553 }, 554 555 flattenFields: function(data, prefix,depth) 556 { 557 if(!prefix) 558 { 559 prefix = ''; 560 } 561 562 if(!depth) 563 { 564 depth = 1; 565 } 566 567 var returnFields = {}; 568 569 var additionalFields = {}; 570 571 if($.isArray(data) || typeof data == 'object') 572 { 573 $.each(data,function(i, obj) 574 { 575 if($.isFunction(obj)) 576 { 577 return true; 578 } 579 580 if(obj && ( $.isArray(obj) || typeof obj == 'object') ) 581 { 582 if(depth > 0) 583 { 584 name = prefix + '[' + i + ']'; 585 } 586 else 587 { 588 name = prefix + i; 589 } 590 591 $.extend(additionalFields, Shout.flattenFields(obj,name,depth+1) ); 592 } 593 else 594 { 595 if(depth > 0) 596 { 597 name = prefix + '[' + i + ']'; 598 } 599 else 600 { 601 name = prefix + i; 602 } 603 604 if (typeof obj == 'boolean') 605 { 606 if (false === obj) 607 { 608 returnFields[name] = 0; 609 } 610 else 611 { 612 returnFields[name] = 1; 613 } 614 } 615 else 616 { 617 returnFields[name] = obj; 618 } 619 } 620 }); 621 } 622 else 623 { 624 return data; 625 } 626 627 $.extend(returnFields,additionalFields); 628 629 return returnFields; 630 }, 631 632 phpSerialize: function(dataArray) 633 { 634 var params = []; 635 636 637 if(dataArray.length) 638 { 639 $.each(dataArray, function(e,obj) { 640 $.each(obj, function(j, data) 641 { 642 var match = phpVariable.exec(j); 643 if(match[2]) 644 { 645 var str = 'actions['+e+']'; 646 647 if(match[1]) 648 { 649 str += '['+match[1]+']'; 650 } 651 652 str += ''+match[2]+'='+data; 653 654 params.push(str); 655 } 656 else 657 { 658 params.push('actions['+e+']['+j+']='+data); 659 } 660 }); 661 }); 662 } 663 664 return params.join('&'); 665 }, 666 667 drawInfoItem: function(content) 668 { 669 var list = []; 670 if($.isArray(content)) 671 { 672 $.each(content,function() 673 { 674 list.push('<li class = "ui-state-info ui-corner-all"><span class = "ui-icon ui-icon-lightbulb"></span><span class="sui-content-text">'+this+'</span></li>'); 675 }); 676 } 677 else 678 { 679 list.push('<li class = "ui-state-info ui-corner-all"><span class = "ui-icon ui-icon-lightbulb"></span><span class="sui-content-text">'+content+'</span></li>'); 680 } 681 682 return $(list.join('')); 683 }, 684 685 drawInfo: function(content) 686 { 687 return $('<div class = "sui-info"></div>').append($(this.drawInfoItem(content)).wrap('<ul></ul>')); 688 }, 689 690 drawSummaryItem: function(content) 691 { 692 var list = []; 693 if($.isArray(content)) 694 { 695 $.each(content,function() 696 { 697 list.push('<li class="ui-state-info ui-corner-all"><div class="sui-summary-content">'+this+'</div></li>'); 698 }); 699 } 700 else 701 { 702 list.push('<li class="ui-state-info ui-corner-all"><div class="sui-summary-content">'+content+'</div></li>'); 703 } 704 705 return $(list.join('')); 706 }, 707 708 showError: function(selector, content) { 709 Shout.hideUIBlock(); 710 711 var exists = false;
712 713 var list; 714 715 if ((list = $(selector).filter('.sui-error').find('> ul')) && list.length) 716 { 717 // If the error already exists, don't re-add it. 718 $(selector).find('> ul > li > .sui-content-text').each(function(){ 719 if($(this).html() == content) 720 { 721 exists = true; 722 return false; 723 } 724 }); 725 726 if (exists) { 727 return; 728 } 729 730 // if you pass in the form element .sui-error container selector i.e. (a .sui-error div/span) 731 list.append(Shout.drawErrorItem(content)); 732 } 733 else if ((list = $(selector).filter('.sui-element.sui-field').find('> .sui-error > ul')) && list.length) 734 { 735 // if you pass in the uielement(sui-element) container `id` - hopefully we are consistent in our structure 736 this.showError(list.parent(), content); 737 } 738 else if ((list = $(selector).filter('.sui-field').find('> .sui-strut-messages > .sui-error > ul')) && list.length) 739 { 740 // if you pass in the FormHelper(sui-field) container `class` - hopefully we are consistent in our structure 741 this.showError(list.parent(), content); 742 } 743 else if ((list = $(selector).closest('.sui-field').filter('.sui-element').find('> .sui-error > ul')) && list.length) 744 { 745 // if you pass in the uielement(sui-element) container `id` - unsafe? 746 this.showError(list.parent(), content); 747 } 748 else if ((list = $(selector).closest('.sui-field').not('.sui-element').find('> .sui-strut-messages > .sui-error > ul')) && list.length) 749 { 750 // if you pass in the FormHelper(sui-field) input `id` - unsafe? 751 this.showError(list.parent(), content); 752 } 753 }, 754 755 hideError: function(selector, content) { 756 757 var list; 758 759 if ((list = $(selector).filter('.sui-error').find('> ul')) && list.length) 760 { 761 if ('undefined' !== typeof content) 762 { 763 $(selector).find('> ul > li > .sui-content-text').each(function(){ 764 if($(this).html() == content) 765 { 766 $(this).closest('li').remove(); 767 } 768 }); 769 } 770 else 771 { 772 $(selector).find('> ul').empty(); 773 } 774 } 775 else if ((list = $(selector).filter('.sui-element.sui-field').find('> .sui-error > ul')) && list.length) 776 { 777 // if you pass in the uielement(sui-element) container `id` - hopefully we are consistent in our structure 778 this.hideError(list.parent(), content); 779 } 780 else if ((list = $(selector).filter('.sui-field').find('> .sui-strut-messages > .sui-error > ul')) && list.length) 781 { 782 // if you pass in the FormHelper(sui-field) container `class` - hopefully we are consistent in our structure 783 this.hideError(list.parent(), content); 784 } 785 else if ((list = $(selector).closest('.sui-field').filter('.sui-element').find('> .sui-error > ul')) && list.length) 786 { 787 // if you pass in the uielement(sui-element) container `id` - unsafe? 788 this.hideError(list.parent(), content); 789 } 790 else if ((list = $(selector).closest('.sui-field').not('.sui-element').find('> .sui-strut-messages > .sui-error > ul')) && list.length) 791 { 792 // if you pass in the FormHelper(sui-field) input `id` - unsafe? 793 this.hideError(list.parent(), content); 794 } 795 }, 796 797 showNotification: function(selector, content, state, dismissable) 798 { 799 var state_id = ''; 800 var state_icon = ''; 801 var state_css = ''; 802 803 var state_global = 0; 804 var state_identifier = ''; 805 var state_vector = 'js'; 806 807 if (undefined == dismissable) 808 { 809 dismissable = true; 810 } 811 812 $.each(Shout.configuration.module.core.notifications.types, function(index, value) 813 { 814 if (value == state) 815 { 816 state_id = index; 817 } 818 } 819 ); 820 821 $.each(Shout.configuration.module.core.notifications.icons, function(index, value) 822 { 823 if (index == state_id) 824 { 825 state_icon = value; 826 } 827 } 828 ); 829 830 $.each(Shout.configuration.module.core.notifications.states, function(index, value) 831 { 832 if (index == state_id) 833 { 834 state_css = value; 835 } 836 } 837 ); 838 839 var dismissable_html = ''; 840 841 if (true === dismissable) 842 {
843 dismissable_html = 844 '<div class="sui-block-notification-dismiss"> \ 845 <span class="ui-icon ui-icon-close"></span> \ 846 </div> \ 847 '; 848 } 849 850 var notification_html = '<div class="sui-block-notification-wrapper"\ 851 data-global="'+state_global+'"\ 852 data-identifier="'+state_identifier+'"\ 853 data-origin="js"\ 854 > \ 855 <div class="ui-widget ui-widget-content ui-corner-all sui-block-notification '+state_css+'"> \ 856 <span class="sui-icon ui-icon '+state_icon+'" style="display:inline-block"></span> \ 857 <span class="sui-description">'+content+'</span> \ 858 '+dismissable_html+' \ 859 </div> \ 860 </div>'; 861 862 $notification_html = $(notification_html); 863 864 $(selector).append($notification_html); 865 866 this.handleNotificationEvents($notification_html); 867 }, 868 869 handleNotificationEvents: function($notification_html) 870 { 871 $notification_html.find('.sui-block-notification').click( 872 function(e) 873 { 874 if ($('.sui-block-notification-dismiss', this).length) 875 { 876 $(this).closest('.sui-block-notification-wrapper').fadeOut('fast'); 877 var data = $(this).closest('.sui-block-notification-wrapper').data(); 878 var searchObj = Shout.buildRequest('Core', 'Shout_Notifier_Util', 'dismiss', {'identifier': data.identifier}); 879 $.post(Shout.configuration.location, searchObj,'json'); 880 } 881 } 882 ); 883 884 $notification_html.find('.sui-block-notification').hover( 885 function (e) 886 { 887 if ($('.sui-block-notification-dismiss', this).length) 888 { 889 $(this).find('.sui-block-notification-dismiss .ui-icon').removeClass('ui-icon-close').addClass('ui-icon-circle-close'); 890 $(this).css('cursor','pointer'); 891 } 892 }, 893 function (e) 894 { 895 if ($('.sui-block-notification-dismiss', this).length) 896 { 897 $(this).find('.sui-block-notification-dismiss .ui-icon').removeClass('ui-icon-circle-close').addClass('ui-icon-close'); 898 $(this).css('cursor','default'); 899 } 900 } 901 ); 902 903 $notification_html.find('.sui-block-notification .sui-description').click( 904 function (e) 905 { 906 e.stopPropagation(); 907 } 908 ); 909 910 $notification_html.find('.sui-block-notification .sui-description').hover( 911 function (e) 912 { 913 if ($(this).closest('.sui-block-notification-wrapper').find('.sui-block-notification-dismiss').length) 914 { 915 $(this).parent().find('.sui-block-notification-dismiss .ui-icon').removeClass('ui-icon-circle-close').addClass('ui-icon-close'); 916 $(this).parent().css('cursor','default'); 917 } 918 919 e.stopPropagation(); 920 }, 921 function (e) 922 { 923 if ($(this).closest('.sui-block-notification-wrapper').find('.sui-block-notification-dismiss').length) 924 { 925 $(this).parent().find('.sui-block-notification-dismiss .ui-icon').removeClass('ui-icon-close').addClass('ui-icon-circle-close'); 926 $(this).parent().css('cursor','pointer'); 927 } 928 929 e.stopPropagation(); 930 } 931 ); 932 }, 933 934 935 clearNotifications: function(selector) 936 { 937 $(selector).find('.sui-block-notification-wrapper[data-origin="js"]').remove(); 938 }, 939 940 drawErrorItem: function(content) 941 { 942 var list = []; 943 if($.isArray(content)) 944 { 945 $.each(content,function() 946 { 947 list.push('<li class = "ui-state-error ui-corner-all"><span class = "ui-icon ui-icon-alert"></span><span class="sui-content-text">'+this+'</span></li>'); 948 }); 949 } 950 else 951 { 952 list.push('<li class = "ui-state-error ui-corner-all"><span class = "ui-icon ui-icon-alert"></span><span class="sui-content-text">'+content+'</span></li>'); 953 } 954 955 return $(list.join('')); 956 }, 957 958 pathToId: function(path) 959 { 960 var pathElements = path.split('/'); 961 962 return pathElements.join('-'); 963 }, 964 965 pathToName: function(path) 966 { 967 var pathElements = path.split('/'); 968 969 var elementString = ''; 970 971 $.each(pathElements,function() 972 { 973 if(elementString.length == 0) 974 { 975 elementString = this; 976 } 977 else 978 { 979 elementString += '['+this+']'; 980 } 981 }); 982 983 return elementString; 984 }, 985 986 nameToId: function(name) 987 { 988 return Shout.str_replace(['[',']'],['-',''],name); 989 }, 990 991 parseCommonParams: function(params) 992 { 993 if(!params) 994 { 995 params = {}; 996 return params; 997 } 998 999 results = {}; 1000 1001 if(params.value) 1002 { 1003 results.value = "value = \""+params.value+"\""; 1004 } 1005 else 1006 { 1007 results.value = ""; 1008 } 1009 1010 if(params.reset != undefined && params.reset == false) 1011 { 1012 results.reset = ""; 1013 } 1014 1015 if(params.css) 1016 { 1017 results.css = params.css; 1018 } 1019 else 1020 { 1021 results.css = ''; 1022 } 1023 1024 if(params.type) 1025 { 1026 results.type = 'type = "'+params.type+'" '; 1027 } 1028 else 1029 { 1030 results.type = ''; 1031 } 1032 1033 if(params.style) 1034 { 1035 results.style = 'style = "'+params.style+'" '; 1036 } 1037 else 1038 { 1039 results.style = ''; 1040 } 1041 1042 if(params.multiple) 1043 { 1044 results.multiple = 'multiple'; 1045 } 1046 else 1047 { 1048 results.multiple = ''; 1049 } 1050 1051 if(params.selected) 1052 { 1053 results.selected = 'selected'; 1054 } 1055 else 1056 { 1057 results.selected = ''; 1058 } 1059 1060 if(params.checked) 1061 { 1062 results.checked = 'checked'; 1063 } 1064 else 1065 { 1066 results.checked = ''; 1067 } 1068 1069 if(params.icon) 1070 { 1071 results.icon = '<span class = "sui-icon '+params.icon+'"></span>'; 1072 } 1073 else 1074 { 1075 results.icon = ''; 1076 } 1077 1078 1079 if(params.href) 1080 { 1081 results.href = "href = \""+params.href+"\""; 1082 } 1083 1084 if(params.target) 1085 { 1086 results.target = "target = \""+params.target+"\""; 1087 } 1088 1089 if(params.title) 1090 { 1091 results.title = "title = \""+params.title+"\""; 1092 } 1093 1094 return results; 1095 }, 1096 1097 drawButton: function(path, label, fieldIconClass, params) 1098 { 1099 var fieldId = this.pathToId(path); 1100 var fieldName = this.pathToName(path); 1101 1102 var pp = this.parseCommonParams(params); 1103 1104 if(params && params.type) 1105 { 1106 pp.type = 'hidden'; 1107 } 1108 else if(params && params.type == 'submit') 1109 { 1110 // Well, we could be using buttons but IE 7 renders them incorrectly, 1111 // depriving us of a semantically useful element. 1112 var hackScript = 'onclick = "$(this).next(\'input\').click();"'; 1113 pp.type = 'submit'; 1114 } 1115 else 1116 { 1117 pp.type = 'hidden'; 1118 } 1119 1120 if(params && params.type == 'submit') 1121 { 1122 pp.name = 'name = "'+fieldName+'"'; 1123 } 1124 else 1125 { 1126 pp.name = ''; 1127 } 1128 1129 if(params && params.click) 1130 { 1131 $('#'+fieldId).click(pp.click); 1132 } 1133 1134 var HTML = '\ 1135 <div class = "sui-control sui-control-button">\ 1136 <a id = "'+fieldId+'" class = "ui-state-default ui-corner-all sui-button '+pp.css+'"\ 1137 '+pp.style+' '+pp.href+' '+pp.title+' '+pp.target+'>\ 1138 '; 1139 1140 if(fieldIconClass && fieldIconClass.length) 1141 { 1142 HTML += '\
1143 <span class = "ui-icon '+fieldIconClass+'"></span>\ 1144 '; 1145 } 1146 1147 if(label.length) 1148 { 1149 HTML += '\ 1150 <span class = "sui-button-label">'+label+'</span>\ 1151 '; 1152 } 1153 1154 HTML += '\ 1155 </a>\ 1156 <input type="'+pp.type+'" '+pp.name+' '+pp.value+' style = "display:none" />\ 1157 </div>'; 1158 1159 return HTML; 1160 }, 1161 1162 addHeaderButton: function(element, icon, callback) 1163 { 1164 //$(element).find('ui-dialog') 1165 }, 1166 1167 multiPaste: function(content, pasteElement, parentSelector, rowSelector, columnSelector) 1168 { 1169 var rowDelimeter = '\n'; 1170 var columnDelimeter = '\t'; 1171 1172 var columnQuery = '.sui-form-field input[type!=hidden], .sui-form-field textarea'; 1173 var rowQuery = '.sui-tree-node'; 1174 var parentQuery = '.sui-tree-root ul > .sui-tree-node'; 1175 1176 var rows = content.split(rowDelimeter); 1177 1178 var resultObj = {}; 1179 1180 $.each(rows,function(i) 1181 { 1182 resultObj[i] = {}; 1183 resultObj[i] = this.split(columnDelimeter); 1184 }); 1185 1186 var rowIndex = 0; 1187 1188 var $parent = $(parentSelector); 1189 1190 // In case we're not in the top right corner of a 2d array of input boxes, we offset the pasting from the cursor position. 1191 var rowOffset = -1; 1192 var columnOffset = -1; 1193 1194 $parent.find(rowSelector).each(function(index) 1195 { 1196 var $currentRow = $(this); 1197 1198 if( ( columnOffset = $currentRow.find(columnSelector).index(pasteElement) ) >= 0 ) 1199 { 1200 rowOffset = index; 1201 1202 return false; 1203 } 1204 1205 }); 1206 1207 var $rows = null; 1208 1209 if(rowOffset != -1) 1210 { 1211 $rows = $(parentSelector).find(rowSelector).slice(rowOffset); 1212 } 1213 else 1214 { 1215 $rows = $(parentSelector).find(rowSelector); 1216 } 1217 1218 $rows.each(function() 1219 { 1220 var columnIndex = 0; 1221 var $columns = null; 1222 1223 if(columnOffset != -1) 1224 { 1225 $columns = $(this).find(columnSelector).slice(columnOffset); 1226 } 1227 else 1228 { 1229 $columns = $(this).find(columnSelector); 1230 } 1231 1232 $columns.each(function() 1233 { 1234 if(!resultObj[rowIndex]) 1235 { 1236 return false; 1237 } 1238 else if(resultObj[rowIndex] == 'undefined') 1239 { 1240 return false; 1241 } 1242 1243 $(this).val(resultObj[rowIndex][columnIndex]); 1244 1245 if(columnIndex > resultObj[rowIndex].length) 1246 { 1247 return false; 1248 } 1249 1250 columnIndex++; 1251 }); 1252 1253 rowIndex++; 1254 }); 1255 }, 1256 1257 expandClass: function(hack) 1258 { 1259 if(hack) 1260 { 1261 return 'sui-expanded-hack'; 1262 } 1263 else 1264 { 1265 return 'sui-expanded'; 1266 } 1267 }, 1268 1269 collapseClass: function(hack) 1270 { 1271 if(hack) 1272 { 1273 return 'sui-collapsed-hack'; 1274 } 1275 else 1276 { 1277 return 'sui-collapsed'; 1278 } 1279 }, 1280 1281 blockToggle: function(blockId, hack) 1282 { 1283 var self = this; 1284 1285 if(self.lastToggle == null ) 1286 { 1287 // Keep track of this because on the next subsequent click (what *would* be the double click) 1288 // we don't want to toggle the main level. 1289 self.lastToggle = Shout.toggleCollapse(blockId,hack); 1290 1291 $(blockId+' > .sui-block-header').on('click.dbltoggle', 1292 function() 1293 { 1294 1295 if(self.lastToggle) 1296 { 1297 Shout.expand($(blockId).find('.sui-toggle'),hack); 1298 } 1299 else 1300 { 1301 Shout.collapse($(blockId).find('.sui-toggle'),hack); 1302 } 1303 } 1304 ); 1305 1306 setTimeout( 1307 function() 1308 { 1309 $(blockId+ '> .sui-block-header').off('click.dbltoggle'); 1310 self.lastToggle = null; 1311 } 1312 ,350); 1313 } 1314 }, 1315 /** 1316 * The hack parameter is used to trigger an alternate implementation that uses 1317 * visibility hidden and position absolute. It was necessitated by plupload. But it turned out 1318 * that still didn't work because changes to the position property also reinitialized the uploader. 1319 */ 1320 toggleCollapse: function($el, hack) 1321 { 1322 if(hack === undefined) 1323 { 1324 hack = false;
1325 } 1326 1327 var $el = $($el); 1328 1329 if($el.hasClass(this.expandClass(hack))) 1330 { 1331 Shout.collapse($el,hack); 1332 return false; 1333 } 1334 else if($el.hasClass(this.collapseClass(hack))) 1335 { 1336 Shout.expand($el,hack); 1337 return true; 1338 } 1339 }, 1340 1341 collapse: function($el,hack) 1342 { 1343 var $el = $($el); 1344 $el.removeClass(this.expandClass(hack)).addClass(this.collapseClass(hack)); 1345 $el.find(' > .sui-toggle-icon').addClass('ui-icon-triangle-1-e').removeClass('ui-icon-triangle-1-se'); 1346 // The following should be accomplished via css. 1347 //$el.find(' > .sui-fieldset').hide(); 1348 }, 1349 1350 expand: function($el,hack) 1351 { 1352 var $el = $($el); 1353 $el.addClass(this.expandClass(hack)).removeClass(this.collapseClass(hack)); 1354 $el.find(' > .sui-toggle-icon').removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-se'); 1355 1356 // The following should be accomplished via css. 1357 //$el.find(' > .sui-fieldset').show(); 1358 }, 1359 1360 hideUIBlock: function(container) 1361 { 1362 if (!container) 1363 { 1364 container = '#sui_content_container'; 1365 } 1366 1367// $(container).unblock(); 1368 $(container).find(' > .sui_content_container_wait').hide('fade'); 1369 }, 1370 1371 showUIBlock: function(container) 1372 { 1373 if (!container) 1374 { 1375 container = '#sui_content_container'; 1376 } 1377 1378 var $container = $(container); 1379 1380 if (0 === $container.find(' > .sui_content_container_wait').length) 1381 { 1382 var wait_container = '\ 1383<div class="sui_content_container_wait">\ 1384</div>\ 1385'; 1386 1387// $container.prepend(wait_container); 1388 $container.append(wait_container); // this forces the z-index to be higher than elements preceding it 1389 } 1390 1391 1392 1393 var refresh = _.debounce( 1394 function(event) 1395 { 1396 Shout.updateUIBlock(container); 1397 } 1398 ); 1399 $(window).off('resize.waitcontainer scroll.waitcontainer', refresh) 1400 .on('resize.waitcontainer scroll.waitcontainer', refresh) 1401 ; 1402 1403// $container.block({message: null}) 1404 $container.find(' > .sui_content_container_wait').show('fade'); 1405 Shout.updateUIBlock(container); 1406 }, 1407 1408 updateUIBlock: function(container) 1409 { 1410 var $container = $(container); 1411 1412 if ($container.length) 1413 { 1414 var $wait_container = $container.find('> .sui_content_container_wait'); 1415 1416 $wait_container 1417// .width($container.outerWidth(true)) 1418// .height($container.outerHeight(true)) 1419 .width($container.width()) 1420 .height($container.height()) 1421 .css('z-index', $container.css( "z-index" ) + 1) 1422 //.css('z-index', 5000) 1423 .css('position', 'absolute') 1424 .position({'my':'left top', 'at':'left top', 'of':$container}) 1425 ; 1426 1427 var $bgColor = $wait_container.parents().filter( 1428 function (index, element) 1429 { 1430 var $element = $(element); 1431 1432 var property_value = $element.css('background-color'); 1433 1434 if ((property_value !== 'rgba(0, 0, 0, 0)') 1435 && (property_value !== 'transparent')) 1436 { 1437 return true; 1438 } 1439 else 1440 { 1441 return false; 1442 } 1443 } 1444 ) 1445 .first() 1446 ; 1447 1448 $wait_container 1449 .css('background-color', $bgColor.css("background-color")); 1450 } 1451 }, 1452 1453 showWaitIndicator: function($query, msg, progress) 1454 { 1455 Shout.showUIBlock($query); 1456 1457 if (0 === $($query).find('> .sui_content_container_wait > .sui_content_container_wait_message_container').length) 1458 { 1459 var wait_container = '\ 1460<div class="rounded-corners sui_content_container_wait_message_container">\ 1461 <div class="sui_content_container_wait_message_block">\
1462 <span class="sui_content_container_wait_indicator"><i class="fa-solid fa-spinner fa-fw fa-spin fa-2xl"></i></span>\ 1463 <span class="sui_content_container_wait_message">Loading</span>\ 1464 </div>\ 1465</div>\ 1466'; 1467 1468 $($query).find('> .sui_content_container_wait').prepend(wait_container); 1469 } 1470 1471 Shout.updateWaitIndicator($query, msg, progress); 1472 }, 1473 1474 hideWaitIndicator: function($query) 1475 { 1476 $($query).find('> .sui_content_container_wait > .sui_content_container_wait_message_container').remove(); 1477 1478 Shout.hideUIBlock($query); 1479 }, 1480 1481 updateWaitIndicator: function($query, message, progress) 1482 { 1483 if (!message) 1484 { 1485 msg = ''; 1486 } 1487 1488 Shout.updateUIBlock($query); 1489 1490 $($query).find(' > .sui_content_container_wait .sui_content_container_wait_message').html(message); 1491 1492 if (progress == undefined) 1493 { 1494 $($query).find(' > .sui_content_container_wait .sui_content_container_wait_progress_bar').hide(); 1495 } 1496 else 1497 { 1498 $($query).find(' > .sui_content_container_wait .sui_content_container_wait_progress_bar').progressbar({value: progress}); 1499 } 1500 1501 Shout.updateUIBlock($query); 1502 }, 1503 1504 showLoadIndicator: function($query, msg, progress) 1505 { 1506 Shout.showUIBlock($query); 1507 1508 if (0 === $($query).find('> .sui_content_container_wait > .sui_content_container_wait_message_container').length) 1509 { 1510 var progress_container = '\ 1511<div class="rounded-corners sui_content_container_wait_message_container">\ 1512 <div class="sui_content_container_wait_message_block">\ 1513 <span class="sui_content_container_wait_indicator"><i class="fa-solid fa-spinner fa-fw fa-spin fa-2xl"></i></span>\ 1514 <span class="sui_content_container_wait_message">Loading</span>\ 1515 </div>\ 1516 <div class="sui_content_container_wait_progress_bar"></div>\ 1517</div>\ 1518'; 1519 1520 $($query).find('> .sui_content_container_wait').prepend(progress_container); 1521 } 1522 1523 Shout.updateLoadIndicator($query, msg, progress); 1524 }, 1525
1526 hideLoadIndicator: function($query) 1527 { 1528 $($query).find('> .sui_content_container_wait > .sui_content_container_wait_message_container').remove(); 1529 1530 Shout.hideUIBlock($query); 1531 }, 1532 1533 updateLoadIndicator: function($query, message, progress) 1534 { 1535 if (!message) 1536 { 1537 msg = ''; 1538 } 1539 1540 Shout.updateUIBlock($query); 1541 1542 $($query).find(' > .sui_content_container_wait .sui_content_container_wait_message').html(message); 1543 1544 if (progress == undefined) 1545 { 1546 $($query).find(' > .sui_content_container_wait .sui_content_container_wait_progress_bar').hide(); 1547 } 1548 else 1549 { 1550 $($query).find(' > .sui_content_container_wait .sui_content_container_wait_progress_bar').progressbar({value: progress}); 1551 } 1552 1553 Shout.updateUIBlock($query); 1554 }, 1555 1556 tooltip: function($el, content, config) 1557 { 1558 var self = this; 1559 1560 config = _tooltipNormalizeConfig(config); 1561 1562 if ('qtip' === config.driver && $.fn.qtip) 1563 { 1564 if (!(typeof content === 'string')) 1565 { 1566 throw 'content should be a string'; 1567 } 1568 1569 var args = { 1570 content: { 1571 'text' : content 1572 } 1573 }; 1574 1575 _tooltip($el, args, config); 1576 1577 return; 1578 } 1579 else if ('tippy' === config.driver && window.tippy) 1580 { 1581 var args = { 1582 content: content 1583 }; 1584 1585 _tooltip($el, args, config); 1586 1587 return; 1588 } 1589 else 1590 { 1591 throw 'invalid driver specified'; 1592 } 1593 1594 throw 'unable to create tooltip'; 1595 }, 1596 1597 callbackTooltip: function($el, callback, config) 1598 { 1599 var self = this; 1600 1601 config = _tooltipNormalizeConfig(config); 1602 1603 if ('qtip' === config.driver && $.fn.qtip) 1604 { 1605 var args = { 1606 content: { 1607 text: function(event, api) 1608 { 1609 var result = callback(); 1610 if (!result || typeof result !== 'object' || typeof result.then !== 'function') 1611 { 1612 throw 'callback must return a Promise or jQuery.Deferred object'; 1613 } 1614 1615 result 1616 .done( 1617 function(data) 1618 { 1619 api.set('content.text', data); 1620 } 1621 ) 1622 .fail( 1623 function(reason) 1624 { 1625 api.set('content.text', reason); 1626 } 1627 ); 1628 1629 return '<div>loading</div>'; 1630 } 1631 } 1632 }; 1633 1634 _tooltip($el, args, config); 1635 1636 return promise; 1637 } 1638 else if ('tippy' === config.driver && window.tippy) 1639 { 1640 var loading = '<i class="far fa-spinner fa-spin"></i>'; 1641 1642 var args = { 1643 content: loading, 1644 animateFill: false, 1645 animation: 'fade', 1646 flipOnUpdate: true, 1647 onShow: function(instance) { 1648 // We can monkey-patch the instance's state object with our own state 1649 if (instance.state.callback === undefined) 1650 { 1651 instance.state.callback = 1652 { 1653 isFetching: false, 1654 canFetch: true, 1655 } 1656 } 1657 1658 // Now we will avoid initiating a new request unless the old one 1659 // finished (`isFetching`). 1660 // We also only want to initiate a request if the tooltip has been 1661 // reset back to Loading... (`canFetch`). 1662 if (instance.state.callback.isFetching || !instance.state.callback.canFetch) 1663 { 1664 return; 1665 } 1666 1667 instance.state.isFetching = true; 1668 instance.state.canFetch = false;
1669 1670 var result = callback(); 1671 if (!result || typeof result !== 'object' || typeof result.then !== 'function') 1672 { 1673 throw 'callback must return a Promise or jQuery.Deferred object'; 1674 } 1675 1676 result 1677 .then( 1678 function(html) 1679 { 1680 instance.setContent(html); 1681 }, 1682 function(error) 1683 { 1684 instance.setContent(error); 1685 } 1686 ) 1687 .always( 1688 function() 1689 { 1690 instance.state.callback.isFetching = false; 1691 } 1692 ) 1693 ; 1694 }, 1695 onHidden: function(instance) { 1696 instance.setContent(loading); 1697 instance.state.callback.canFetch = true; 1698 } 1699 }; 1700 1701 _tooltip($el, args, config); 1702 1703 return; 1704 } 1705 else 1706 { 1707 throw 'invalid driver specified'; 1708 } 1709 1710 throw 'unable to create tooltip'; 1711 }, 1712 1713 ajaxTooltip: function($el, url, config) 1714 { 1715 // TODO: it would be nice to handle json responses... maybe we need our own jqueryui object to wrap the tooltip 1716 // allowing us to specify handlers, OR maybe we could use jquery deferred/promises, OR maybe add some properties 1717 // to the config object to handle it 1718 if (!(typeof url === 'string')) 1719 { 1720 throw 'url should be a string'; 1721 } 1722 1723 var callback = 1724 function() 1725 { 1726 return $.ajax({ 1727 url: url 1728 }) 1729 .done( 1730 function(data, textStatus, jqXHR) 1731 { 1732 var ct = jqXHR.getResponseHeader("content-type") || ""; 1733 if (ct.indexOf('html') <= -1) 1734 { 1735 throw 'ajax response must be text/html'; 1736 } 1737 1738 return data; 1739 } 1740 ) 1741 .fail( 1742 function(xhr, status, error) 1743 { 1744 return status + ': ' + error; 1745 } 1746 ); 1747 } 1748 ; 1749 1750 return Shout.callbackTooltip($el, callback, config); 1751 }, 1752 1753 popover: function($el, content, config) 1754 { 1755 var converted_config = $.extend(true, {}, config, 1756 { 1757 driver_config: 1758 { 1759 tippy: 1760 { 1761 interactive:true 1762 } 1763 } 1764 } 1765 ); 1766 return Shout.tooltip($el, content, converted_config); 1767 }, 1768 1769 callbackPopover: function($el, callback, config) 1770 { 1771 var converted_config = $.extend(true, {}, config, 1772 { 1773 driver_config: 1774 { 1775 tippy: 1776 { 1777 interactive:true 1778 } 1779 } 1780 } 1781 ); 1782 return Shout.callbackTooltip($el, callback, converted_config); 1783 1784 }, 1785 1786 ajaxPopover: function($el, url, config) 1787 { 1788 var converted_config = $.extend(true, {}, config, 1789 { 1790 driver_config: 1791 { 1792 tippy: 1793 { 1794 interactive:true 1795 } 1796 } 1797 } 1798 ); 1799 return Shout.ajaxTooltip($el, url, converted_config); 1800 1801 }, 1802 1803 autoSlugify: function($titleField, $slugField, func) 1804 { 1805 if (typeof func === 'undefined') 1806 { 1807 func = this._transformToSlug; 1808 } 1809 1810 if (typeof func !== 'function') 1811 { 1812 throw new TypeError('Expected a function'); 1813 } 1814 1815 $($titleField).on('keydown', function() 1816 { 1817 var currentTitle = $(this).val(); 1818 1819 var safeTitle = func.call(this, currentTitle, true); 1820 1821 if (safeTitle == $($slugField).val()) 1822 { 1823 $($titleField).one('keyup',function() 1824 { 1825 var currentTitle = $(this).val(); 1826 1827 var safeTitle = func.call(this, currentTitle, true); 1828 1829 if ($($slugField).attr('maxlength') > 0) 1830 { 1831 if ($($slugField).attr('maxlength') >= safeTitle.length) 1832 { 1833 $($slugField).val(safeTitle); 1834 } 1835 else 1836 { 1837 $($slugField).val(safeTitle.substring(0, $($slugField).attr('maxlength'))); 1838 } 1839 } 1840 else 1841 { 1842 $($slugField).val(safeTitle); 1843 } 1844 }); 1845 } 1846 }); 1847 1848 $($slugField).on('keydown', function() 1849 { 1850 $(this).one('keyup', function() 1851 { 1852 var currentSlug = $(this).val(); 1853 var safeSlug = func.call(this, currentSlug, false); 1854 $(this).val(safeSlug); 1855 }); 1856 }); 1857 1858 $($slugField).on('change', function() 1859 { 1860 var currentSlug = $(this).val(); 1861 var safeSlug = func.call(this, currentSlug, true); 1862 $(this).val(safeSlug); 1863 }); 1864 }, 1865 1866 transformToSlug: function(str) 1867 { 1868 return this._transformToSlug(str); 1869 }, 1870 1871 _transformToSlug: function(str, trimhyphens) 1872 {
1873 if (typeof trimhyphens === 'undefined') 1874 { 1875 trimhyphens = true; 1876 } 1877 1878 var slug = str; 1879 1880 slug = slug.trim() 1881 .replace(/[^a-zA-Z0-9-]+/gi,'-') 1882 .replace(/-+/gi,'-') 1883 .toLowerCase() 1884 ; 1885 1886 if (trimhyphens) 1887 { 1888 slug = slug.replace(/^-+|-+$/g,''); 1889 } 1890 1891 return slug; 1892 }, 1893 1894 /** 1895 * Used to construct a request for our RPC system. 1896 * 1897 * @param module the primary module 1898 * @param an optional secondary module. Should have a handleAction function. 1899 * @param params Additional parameters. These get flattened into a nested JSON object 'actionParams'. 1900 */ 1901 buildRequest: function(module, handler, mode, params) 1902 { 1903 var searchObj = { 1904 'module' : module, 1905 'handler' :handler, 1906 'action': Shout.configuration.action, 1907 'mode' : mode 1908 }; 1909 1910 return $.extend(searchObj, Shout.flattenFields(params,'actionParams') ); 1911 }, 1912 1913 /** 1914 * @param img The original url (relative) to the image. 1915 * @param params The thumbnail configuration. Maps to Image::generateThumb configuration parameters (width, height, watermark, crop). 1916 */ 1917 requestThumbnail: function(img, params) 1918 { 1919 if (!params) 1920 { 1921 params = { }; 1922 } 1923 1924 if ('' == img) 1925 { 1926 return $.when({}); 1927 } 1928 1929 var defaults = { 1930 'img':img, 1931 'v': 3, 1932 }; 1933 1934 var data = $.extend(defaults, params); 1935 1936 var url = [ 1937 Shout.configuration.absRoot, 1938 '/', 1939 'image.php' 1940 ].join(''); 1941 1942 var details = null; 1943 1944 return $.ajax( 1945 { 1946// headers: { 1947// "Accept" : "application/json; charset=utf-8", 1948// "Content-Type": "application/json; charset=utf-8" 1949// }, 1950 1951 1952 type: "GET", 1953// contentType: "application/json; charset=utf-8", 1954 1955 dataType: 'json', 1956// accepts: {json: 'application/json'}, 1957 1958 async: true, 1959 1960 url: url, 1961 data: data 1962 } 1963 ); 1964 }, 1965 /** 1966 * Used to screen ajax request data for new content. The purpose is to prevent 1967 * repeatedly including scripts and css files that already exist in the page. 1968 * 1969 * These are, by convention, provided regardless. For example, a userbrowser might have an embededded 1970 * GroupBrowser that wasn't present for the initial page load. We'll need to include the scripts initially. 1971 * But subsequently, it's wasteful and leads to odd behaviors. 1972 */ 1973 filterContent: function(data, params) 1974 { 1975 if (!params) 1976 { 1977 params = {}; 1978 } 1979 1980 var defaults = { 1981 // Filter external sources we've already loaded. 1982 filterExistingExternal: true, 1983 // Filter <script>...</script> tags (non-external). 1984 filterNonExternalScripts: false 1985 }; 1986 1987 // Configurable operation. 1988 params = $.extend(defaults, params); 1989 1990 if (!data) 1991 { 1992 return undefined; 1993 } 1994 1995 var $data = $(data); 1996 1997 var $content = $data.not('script, link'); 1998 var $scripts = $data.filter('script'); 1999 var $css = $data.filter('link'); 2000 2001 $scripts = $scripts.map( 2002 function() 2003 { 2004 var src = $(this).attr('src'); 2005 2006 if (src 2007 && params.filterExistingExternal 2008 && !Shout.isScriptLoaded(src)) 2009 { 2010 return this; 2011 } 2012 else if (!src 2013 && !params.filterNonExternalScripts) 2014 { 2015 return this; 2016 } 2017 } 2018 ); 2019 2020 $css = $css.map( 2021 function() 2022 { 2023 if (!$('link[href="'+$(this).attr('href')+'"]').length) 2024 { 2025 return this; 2026 } 2027 } 2028 ); 2029 2030 var $filtered_data = $content.add($scripts).add($css); 2031 2032 var filtered_data = $.map($filtered_data, function(val, index) { 2033 2034 var str = ''; 2035 2036 if (3 == val.nodeType) 2037 { 2038 str = val.nodeValue; 2039 } 2040 else 2041 { 2042 str = Shout.outerHTML(val); 2043// str = val.outerHTML; 2044 } 2045 2046 return str; 2047 }).join(""); 2048 2049 return filtered_data; 2050 }, 2051 /** 2052 * Used to screen ajax request data for new content. The purpose is to prevent 2053 * repeatedly including scripts and css files that already exist in the page. 2054 * 2055 * These are, by convention, provided regardless. For example, a userbrowser might have an embededded 2056 * GroupBrowser that wasn't present for the initial page load. We'll need to include the scripts initially. 2057 * But subsequently, it's wasteful and leads to odd behaviors. 2058 */ 2059 handleContent: function(data, params) 2060 { 2061 if (!params) 2062 { 2063 params = {}; 2064 } 2065 2066 var defaults = { 2067 debug: false, 2068 use_style_promise: true//, 2069 //excludeLoadedScripts: true, 2070 //excludeLoadedStyles: true, 2071 }; 2072 2073 // Configurable operation. 2074 params = $.extend(defaults, params); 2075 2076 var _promise = new Promise( 2077 function (resolve, reject) 2078 { 2079 if (!data) 2080 { 2081 reject('data is undefined'); 2082 } 2083 2084 var $data = $(data); 2085 2086 var $content = $data.not('script, link'); 2087 var $scripts = $data.filter('script'); 2088 var $css = $data.filter('link'); 2089 2090 var filtered_data = ''; 2091 2092 var $new_external_script_uris = $scripts.map( 2093 function() 2094 { 2095 var src = $(this).attr('src'); 2096 2097 if (src) 2098 { 2099 return src; 2100 } 2101 } 2102 ).map( 2103 function() 2104 { 2105 if (false === Shout.isScriptLoaded(this.valueOf())) 2106 { 2107 return this.valueOf(); 2108 } 2109 } 2110 ); 2111 2112 if (true === params.debug) 2113 { 2114 var z = {}; 2115 z.es = $new_external_script_uris; 2116 } 2117 2118 var $inline_scripts = $scripts.map( 2119 function() 2120 { 2121 var src = $(this).attr('src'); 2122 2123 if (!src) 2124 { 2125 return this; 2126 } 2127 } 2128 ); 2129 2130 if (true === params.debug) 2131 { 2132 z.is = $inline_scripts.map( 2133 function() 2134 { 2135 return $(this).html(); 2136 } 2137 ); 2138 } 2139 2140 if (true === params.use_style_promise) 2141 { 2142 var $new_css = $css.map( 2143 function() 2144 { 2145 if (!$('link[href="'+$(this).attr('href')+'"]').length) 2146 { 2147 return $(this).attr('href'); 2148 } 2149 } 2150 ).get(); 2151 2152 if (true === params.debug) 2153 { 2154 z.nc = $new_css 2155 } 2156 2157 // https://javascript.info/promise-chaining 2158 // https://davidwalsh.name/javascript-loader 2159 var $new_css_promises = $.map($new_css, 2160 function(val, i) 2161 { 2162 var _stylePromise = load.css(val); 2163 2164 return _stylePromise; 2165 } 2166 ); 2167 2168 var _stylesPromise = Promise.all($new_css_promises); 2169 } 2170 else 2171 { 2172 var $new_css = $css.map( 2173 function() 2174 { 2175 if (!$('link[href="'+$(this).attr('href')+'"]').length) 2176 { 2177 return $(this); 2178 } 2179 } 2180 ); 2181 2182 if (true === params.debug) 2183 { 2184 z.nc = $new_css.map( 2185 function() 2186 { 2187 return $(this).attr('href'); 2188 } 2189 ); 2190 } 2191 2192 var filtered_css = $.map($new_css, function(val, index) { 2193 2194 var str = ''; 2195 2196 if (3 == val.nodeType) 2197 { 2198 str = val.nodeValue; 2199 } 2200 else 2201 { 2202 str = Shout.outerHTML(val); 2203 } 2204 2205 return str; 2206 2207 }).join(""); 2208 2209 $(document).find('html > head').append(filtered_css); 2210 2211 var _stylesPromise = new Promise( 2212 function(resolve, reject) 2213 { 2214 resolve(); 2215 } 2216 ); 2217 } 2218 2219 if (params.debug) 2220 { 2221 console.log(z); 2222 } 2223 2224 var $filtered_data = $content.add($inline_scripts)/*.add($new_css)*/; 2225 2226 var filtered_data = $.map($filtered_data, function(val, index) { 2227 2228 var str = ''; 2229 2230 if (3 == val.nodeType) 2231 { 2232 str = val.nodeValue; 2233 } 2234 else 2235 { 2236 str = Shout.outerHTML(val); 2237 } 2238 2239 return str; 2240 2241 }).join(""); 2242 2243 var _scriptsPromise = Shout.getScripts($new_external_script_uris); 2244 2245 Promise.all([ 2246 _stylesPromise, 2247 _scriptsPromise 2248 ]).then( 2249 function(result) 2250 { 2251 result.filtered_data = filtered_data; 2252 2253 resolve(result); // resolve _promise 2254 }, 2255 function(reason) 2256 { 2257 reject(reason); // reject _promise 2258 } 2259 ); 2260 } 2261 ); 2262 2263 return _promise; 2264 }, 2265 2266 filterContentInlineScripts: function(data, params) 2267 { 2268 if (!params) 2269 { 2270 params = {}; 2271 } 2272 2273 var defaults = { 2274 }; 2275 2276 // Configurable operation. 2277 params = $.extend(defaults, params); 2278 2279 if (!data) 2280 { 2281 return undefined; 2282 } 2283 2284 var $data = $(data); 2285 2286 var $scripts = $data.filter('script'); 2287 2288 $scripts = $scripts.map( 2289 function() 2290 { 2291 var src = $(this).attr('src'); 2292 2293 if (!src) 2294 { 2295 return this; 2296 } 2297 } 2298 ); 2299 2300 var filtered_data = $.map($scripts, function(val, index) { 2301 2302 var str = Shout.outerHTML(val); 2303 2304 return str; 2305 }).join(""); 2306 2307 return filtered_data; 2308 }, 2309 2310 filterContentScripts: function(data, params) 2311 { 2312 if (!params) 2313 { 2314 params = {}; 2315 } 2316 2317 var defaults = { 2318 excludeLoadedScripts: true 2319 }; 2320 2321 // Configurable operation. 2322 params = $.extend(defaults, params); 2323 2324 if (!data) 2325 { 2326 return undefined; 2327 } 2328 2329 var $data = $(data); 2330 2331 var $scripts = $data.filter('script'); 2332 2333 $scripts = $scripts.map( 2334 function() 2335 { 2336 var src = $(this).attr('src'); 2337 2338 if (true === params.excludeLoadedScripts) 2339 { 2340 if (src 2341 && !Shout.isScriptLoaded(src)) 2342 { 2343 return src; 2344 } 2345 } 2346 else 2347 { 2348 if (src) 2349 { 2350 return src; 2351 } 2352 } 2353 } 2354 ); 2355 2356 return $scripts; 2357 }, 2358 2359 //@see: http://stackoverflow.com/questions/11803215/how-to-include-multiple-js-files-using-jquery-getscript-method 2360 //@see: http://stackoverflow.com/questions/23018012/making-multiple-scripts-loader-with-jquery-deferred-object-am-i-doing-it-right 2361 //@see: http://stackoverflow.com/questions/11803215/how-to-include-multiple-js-files-using-jquery-getscript-method 2362 //@see: http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use 2363 getScripts: function(url, params) { 2364 2365 if (!params) 2366 { 2367 params = {}; 2368 } 2369 2370 // Configurable operation. 2371 _params = $.extend(params, 2372 { 2373 "path": "", 2374 "sequential": true, 2375 "ajax": { 2376 'async': true, 2377 'cache': false 2378 } 2379 } 2380 ); 2381 2382 if (url === undefined) 2383 { 2384 return undefined; 2385 } 2386 2387 var _urls = (url instanceof Array || url instanceof jQuery) ? url : [url]; 2388 var _promises = []; 2389 2390 // https://promisesaplus.com/ 2391 // https://gist.github.com/justinjmoses/18cdb0c5f65314b8e3d
239153f84d2b76b56 2392 // https://developers.google.com/web/fundamentals/primers/promises 2393 2394 // http://bluebirdjs.com/docs/api/deferred-migration.html 2395 var defer = function () { 2396 'use strict'; 2397 2398 var result, resolve, reject; 2399 2400 return { 2401 resolve: function (value) { 2402 if (resolve) { 2403 resolve(value); 2404 } else { 2405 result = result || new Promise(function (r) { r(value); }); 2406 } 2407 }, 2408 2409 reject: function (reason) { 2410 if (reject) { 2411 reject(reason); 2412 } else { 2413 result = result || new Promise(function (_, j) { j(reason); }); 2414 } 2415 }, 2416 2417 promise: new Promise(function (r, j) { 2418 if (result) { 2419 r(result); 2420 } else { 2421 resolve = r; 2422 reject = j; 2423 } 2424 }) 2425 }; 2426 }; 2427 2428 var _rootDeferred = defer(); 2429 var _rootPromise = _rootDeferred.promise; 2430 2431 var _promise = _rootPromise; 2432 2433 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Flags_and_bitmasks 2434 var FLAG_A = 1; // 00001 2435 var FLAG_B = 2; // 00010 2436 var FLAG_C = 4; // 00100 2437 var FLAG_D = 8; // 01000 2438 var FLAG_E = 16; // 10000 2439 2440 var debug = false; // /*FLAG_A | FLAG_B | */FLAG_C | FLAG_D; 2441 2442 _promises.push(_rootPromise); 2443 2444 if (true === _params.sequential) 2445 { 2446 var _prevPromise = _promise; 2447 2448 $.each(_urls, 2449 function(index, url) 2450 { 2451 var _resolved_url = (_params.path||"") + url; 2452 2453 if (!Shout.isScriptLoaded(_resolved_url)) 2454 { 2455 if (debug & FLAG_E) 2456 { 2457 console.groupCollapsed(url); 2458 console.log('%c_prevPromise%O', "color: silver", _prevPromise); 2459 console.groupEnd(); 2460 } 2461 2462 var _promise = _prevPromise.then( 2463 function(value) 2464 { 2465 if (debug & FLAG_A) 2466 { 2467 console.groupCollapsed("%c%i:resolve filter: %s", "color: blue", index, _resolved_url); 2468 console.log('this', this); 2469 console.log('arguments', arguments); 2470 console.groupEnd(); 2471 } 2472 2473 return Shout.getScript(_resolved_url, _params); 2474 }, 2475 function(reason) 2476 { 2477 if (debug & FLAG_A) 2478 { 2479 console.groupCollapsed("%c%i:resolve filter: %s", "color: blue", index, _resolved_url); 2480 console.log('this', this); 2481 console.log('arguments', arguments); 2482 console.groupEnd(); 2483 } 2484 2485 console.error(reason); 2486 2487 return reason; 2488 } 2489 ); 2490 2491 if (debug & FLAG_B) 2492 { 2493 _promise.then( 2494 function() 2495 { 2496 console.groupCollapsed("%c%i:done: %s", "color: blue", index, _resolved_url); 2497 console.log('this', this); 2498 console.log('arguments', arguments); 2499 console.groupEnd(); 2500 } 2501 ).catch( 2502 function() 2503 { 2504 console.groupCollapsed("%c%i:fail: %s", "color: blue", index, _resolved_url); 2505 console.log('this', this); 2506 console.log('arguments', arguments); 2507 console.groupEnd(); 2508 } 2509 ).finally( 2510 function() 2511 { 2512 console.groupCollapsed("%c%i:always: %s", "color: blue", index, _resolved_url); 2513 console.log('this', this); 2514 console.log('arguments', arguments); 2515 console.groupEnd(); 2516 } 2517 ); 2518 2519 _promise.index = index; 2520 _promise.url = _resolved_url; 2521 } 2522 2523 // even though some promises might get rejected or resolved and the chain will end we still 2524 // test via when below to properly return a proper state and promise... maybe we should use 2525 // Promise.allSettled instead 2526 _promises.push(_promise); 2527 2528 _prevPromise = _promise; 2529 } 2530 } 2531 ); 2532 } 2533 else 2534 { 2535 _promises = $.map(_urls, 2536 function(url) 2537 { 2538 var _resolved_url = (_params.path||"") + url; 2539 2540 if (!Shout.isScriptLoaded(_resolved_url)) 2541 { 2542 return Shout.getScript(_resolved_url, _params); 2543 } 2544 } 2545 ); 2546 } 2547 2548 if (debug & FLAG_C) 2549 { 2550 _prevPromise.then( 2551 function() 2552 { 2553 console.groupCollapsed("%c _prevPromise done", "color: green"); 2554 console.log('this', this); 2555 console.log('arguments', arguments); 2556 console.groupEnd(); 2557 } 2558 ).catch( 2559 function() 2560 { 2561 console.groupCollapsed("%c _prevPromise fail", "color: green"); 2562 console.log('this', this); 2563 console.log('arguments', arguments); 2564 console.groupEnd(); 2565 } 2566 ).finally( 2567 function() 2568 { 2569 console.groupCollapsed("%c _prevPromise always", "color: green"); 2570 console.log('this', this); 2571 console.log('arguments', arguments); 2572 console.groupEnd(); 2573 } 2574 ); 2575 _rootPromise.then( 2576 function() 2577 { 2578 console.groupCollapsed("%c _rootPromise done", "color: green"); 2579 console.log('this', this); 2580 console.log('arguments', arguments); 2581 console.groupEnd(); 2582 } 2583 ).catch( 2584 function() 2585 { 2586 console.groupCollapsed("%c _rootPromise fail", "color: green"); 2587 console.log('this', this); 2588 console.log('arguments', arguments); 2589 console.groupEnd(); 2590 } 2591 ).finally( 2592 function() 2593 { 2594 console.groupCollapsed("%c _rootPromise always", "color: green"); 2595 console.log('this', this); 2596 console.log('arguments', arguments); 2597 console.groupEnd(); 2598 } 2599 ); 2600 } 2601 2602 // ensures dom is ready 2603 _promises.push( 2604 Promise.resolve( 2605 $.Deferred( 2606 function(deferred) 2607 { 2608 $(deferred.resolve); 2609 } 2610 ).promise() 2611 ) 2612 ); 2613 2614 // this tests to make sure all the promises are resolved 2615 var _when = Promise.all(_promises); 2616 2617 if (debug & FLAG_D) 2618 { 2619 _when.then( 2620 function() 2621 { 2622 console.groupCollapsed("%c _when done", "color: purple"); 2623 console.log('this', this); 2624 console.log('arguments', arguments); 2625 console.groupEnd(); 2626 } 2627 ).catch( 2628 function() 2629 { 2630 console.groupCollapsed("%c _when fail", "color: purple"); 2631 console.log('this', this); 2632 console.log('arguments', arguments); 2633 console.groupEnd(); 2634 } 2635 ).finally( 2636 function() 2637 { 2638 console.groupCollapsed("%c _when always", "color: purple"); 2639 console.log('this', this); 2640 console.log('arguments', arguments); 2641 console.groupEnd(); 2642 } 2643 ); 2644 } 2645 2646 // this delays the start of the ajax calls until all of the promises are in place 2647 _rootDeferred.resolve(); 2648 2649 return _when; 2650 }, 2651 getScriptsOld: function(url, params) { 2652 2653 if (!params) 2654 { 2655 params = {}; 2656 } 2657 2658 // Configurable operation. 2659 _params = $.extend(params, 2660 { 2661 "path": "", 2662 "sequential": true, 2663 "ajax": { 2664 'async': true, 2665 'cache': false 2666 } 2667 } 2668 ); 2669 2670 if (url === undefined) 2671 { 2672 return undefined; 2673 } 2674 2675 var _urls = (url instanceof Array || url instanceof jQuery) ? url : [url]; 2676 var _promises = []; 2677 2678 var _deferred = new $.Deferred(); 2679 var _manualDeferred = _deferred; 2680 var _manualPromise = _deferred.promise(); 2681 var _promise = _manualPromise; 2682 2683 var debug = false;
2684 2685 _promises.push(_manualPromise); 2686 2687 if (true === _params.sequential) 2688 { 2689 var _prevPromise = _promise; 2690 var _ajaxPromises = []; 2691 2692 $.each(_urls, 2693 function(index, url) 2694 { 2695 var _resolved_url = (_params.path||"") + url; 2696 2697 if (!Shout.isScriptLoaded(_resolved_url)) 2698 { 2699 if (true === debug) 2700 { 2701 console.groupCollapsed(url); 2702 console.log('%c_prevPromise%O', "color: silver", _prevPromise); 2703 console.groupEnd(); 2704 } 2705 2706 var _promise = _prevPromise.then( 2707 function() 2708 { 2709 if (true === debug) 2710 { 2711 console.groupCollapsed("%c%i:resolve filter: %s", "color: blue", index, _resolved_url); 2712 console.log('this', this); 2713 console.log('arguments', arguments); 2714 console.groupEnd(); 2715 } 2716 2717 return Shout.getScript(_resolved_url, _params); 2718 }, 2719 function() 2720 { 2721 2722 if (true === debug) 2723 { 2724 console.groupCollapsed("%c%i:reject filter: %s", "color: blue", index, _resolved_url); 2725 console.log('this', this); 2726 console.log('arguments', arguments); 2727 console.groupEnd(); 2728 } 2729 2730 return false; 2731 } 2732 ); 2733 2734 if (true === debug) 2735 { 2736 _promise.progress( 2737 function() 2738 { 2739 console.groupCollapsed("%c%i:progress: %s", "color: blue", index, _resolved_url); 2740 console.log('this', this); 2741 console.log('arguments', arguments); 2742 console.groupEnd(); 2743 } 2744 ) 2745 .done( 2746 function() 2747 { 2748 console.groupCollapsed("%c%i:done: %s", "color: blue", index, _resolved_url); 2749 console.log('this', this); 2750 console.log('arguments', arguments); 2751 console.groupEnd(); 2752 } 2753 ).fail( 2754 function() 2755 { 2756 console.groupCollapsed("%c%i:fail: %s", "color: blue", index, _resolved_url); 2757 console.log('this', this); 2758 console.log('arguments', arguments); 2759 console.groupEnd(); 2760 } 2761 ).always( 2762 function() 2763 { 2764 console.groupCollapsed("%c%i:always: %s", "color: blue", index, _resolved_url); 2765 console.log('this', this); 2766 console.log('arguments', arguments); 2767 console.groupEnd(); 2768 } 2769 ); 2770 2771 _promise.index = index; 2772 _promise.url = _resolved_url; 2773 } 2774 2775 _ajaxPromises.push(_promise); 2776 2777 // even though some promises might get rejected or resolved and the chain will end we still 2778 // test via when below to properly return a proper state and promise 2779 _promises.push(_promise); 2780 2781 _prevPromise = _promise; 2782 } 2783 } 2784 ); 2785 2786 if (true === debug) 2787 { 2788 _prevPromise.progress( 2789 function() 2790 { 2791 console.groupCollapsed("%cprogress", "color: purple"); 2792 console.log('this', this); 2793 console.log('arguments', arguments); 2794 console.groupEnd(); 2795 } 2796 ) 2797 .done( 2798 function() 2799 { 2800 console.groupCollapsed("%cdone", "color: purple"); 2801 console.log('this', this); 2802 console.log('arguments', arguments); 2803 console.groupEnd(); 2804 } 2805 ).fail( 2806 function() 2807 { 2808 console.groupCollapsed("%cfail", "color: purple"); 2809 console.log('this', this); 2810 console.log('arguments', arguments); 2811 console.groupEnd(); 2812 } 2813 ).always( 2814 function() 2815 { 2816 console.groupCollapsed("%calways", "color: purple"); 2817 console.log('this', this); 2818 console.log('arguments', arguments); 2819 console.groupEnd(); 2820 } 2821 ); 2822 2823 _manualDeferred.progress( 2824 function() 2825 { 2826 console.groupCollapsed("%cprogress", "color: green"); 2827 console.log('this', this); 2828 console.log('arguments', arguments); 2829 console.groupEnd(); 2830 } 2831 ) 2832 .done( 2833 function() 2834 { 2835 console.groupCollapsed("%cdone", "color: green"); 2836 console.log('this', this); 2837 console.log('arguments', arguments); 2838 console.groupEnd(); 2839 } 2840 ).fail( 2841 function() 2842 { 2843 console.groupCollapsed("%cfail", "color: green"); 2844 console.log('this', this); 2845 console.log('arguments', arguments); 2846 console.groupEnd(); 2847 } 2848 ).always( 2849 function() 2850 { 2851 console.groupCollapsed("%calways", "color: green"); 2852 console.log('this', this); 2853 console.log('arguments', arguments); 2854 console.groupEnd(); 2855 } 2856 ); 2857 } 2858 } 2859 else 2860 { 2861 _promises = $.map(_urls, 2862 function(url) 2863 { 2864 var _resolved_url = (_params.path||"") + url; 2865 2866 if (!Shout.isScriptLoaded(_resolved_url)) 2867 { 2868 return Shout.getScript(_resolved_url, _params); 2869 } 2870 } 2871 ); 2872 } 2873 2874 // ensures dom is ready 2875 _promises.push($.Deferred( 2876 function(deferred) 2877 { 2878 $(deferred.resolve); 2879 } 2880 ).promise() 2881 ); 2882 2883 if (true === debug) 2884 { 2885 console.log('%cpromises: %O', "color: red;",_promises); 2886 } 2887 2888 // this tests to make sure all the promises are resolved 2889 var _when = $.when.apply($, _promises); 2890 2891 // this delays the start of the ajax calls until all of the promises are in place 2892 _manualDeferred.resolve(); 2893 2894 return _when; 2895 }, 2896 2897 getScript: function(url, params) 2898 { 2899 var self = this; 2900 2901 _params = $.extend(params, 2902 { 2903 "ajax": { 2904 'async': true, 2905 'cache': false 2906 } 2907 } 2908 ); 2909 2910 $method = 'use_internal'; // use_system_js, use_jquery, use_internal 2911 2912 if ('use_system_js' === $method) 2913 { 2914 var _ajaxPromise = SystemJS.import(url) 2915 .then( 2916 function () { 2917 Shout.scriptsloaded.push(url); 2918 }, 2919 function(reason) { 2920 console.log(reason); 2921 } 2922 ); 2923 } 2924 else if ('use_jquery' === $method) 2925 { 2926 var _ajaxPromise = $.ajax( 2927 { 2928 url: url, 2929 dataType: "script", 2930 async: _params.ajax.async, 2931 cache: _params.ajax.cache 2932 } 2933 ); 2934 2935 _ajaxPromise = Promise.resolve(_ajaxPromise); 2936 2937 _ajaxPromise.then( 2938 function (result) { 2939 Shout.scriptsloaded.push(url); 2940 }, 2941 function(reason) { 2942 console.error(reason); 2943 } 2944 ); 2945 } 2946 else if ('use_internal' === $method) 2947 { 2948 _ajaxPromise = load.js(url); 2949 2950 _ajaxPromise.then( 2951 function (result) { 2952 Shout.scriptsloaded.push(url); 2953 }, 2954 function(reason) { 2955 console.error(reason); 2956 } 2957 ); 2958 } 2959 else 2960 {
2961 throw 'invalid getScript method `'+$method+'`'; 2962 } 2963 2964 2965 return _ajaxPromise; 2966 }, 2967 2968 2969 isScriptLoaded: function(url) { 2970 2971 var self = this; 2972 2973 if (0 <= $.inArray(url, Shout.scriptsloaded)) 2974 { 2975 //console.log('already loaded var: '+url); 2976 return true; 2977 } 2978 2979 if ($('script[src="'+url+'"]').length) 2980 { 2981 //console.log('already loaded src: '+url); 2982 return true; 2983 } 2984 2985 //console.log('not loaded: '+url); 2986 return false; 2987 }, 2988 2989 outerHTML: function(el) { 2990 2991 var $el = null; 2992 2993 if (el instanceof jQuery) 2994 { 2995 $el = el; 2996 } 2997 else 2998 { 2999 $el = $(el); 3000 } 3001 3002 return (!$el.length) 3003 ? $el 3004 : ($el.get(0).outerHTML || ( 3005 function($el2){ 3006 var div = document.createElement('div'); 3007 div.appendChild($el2.cloneNode(true)); 3008 var contents = div.innerHTML; 3009 div = null; 3010 return contents; 3011 })($el.get(0))); 3012 }, 3013 3014 /** 3015 * Applies file input styling hackishness. See scripts/walkthrough.php 3016 */ 3017 fileStyle: function($el){ 3018 var $file = $($el).find('.current_file'); 3019 var $cabinet = $($el).find('.cabinet'); 3020 var $input = $cabinet.find('input'); 3021 3022 var posFile = $file.position(); 3023 var posCabinet = $cabinet.position(); 3024 3025 var left = (posCabinet.left - posFile.left) * -1; 3026 3027 $input.attr('size', 25); 3028 $input.position({ 3029 my:'top left', 3030 at:'top left', 3031 of:$file 3032 }); 3033 3034 $input.css('opacity',0); 3035 $input.css('zIndex',2000); 3036 $input.css('left',left); 3037 }, 3038 3039 createCookie: function(name,value,days) { 3040 if(typeof days === 'undefined') 3041 { 3042 days = 10000; 3043 } 3044 3045 if (days) 3046 { 3047 var date = new Date(); 3048 date.setTime(date.getTime()+(days*24*60*60*1000)); 3049 var expires = "; expires="+date.toGMTString(); 3050 } 3051 else 3052 { 3053 var expires = ""; 3054 } 3055 3056 document.cookie = name+"="+value+expires+"; path=/"; 3057 }, 3058 3059 readCookie: function(name) { 3060 var nameEQ = name + "="; 3061 var ca = document.cookie.split(';'); 3062 for(var i=0;i < ca.length;i++) { 3063 var c = ca[i]; 3064 while (c.charAt(0)==' ') c = c.substring(1,c.length); 3065 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); 3066 } 3067 return null; 3068 }, 3069 3070 eraseCookie: function(name) { 3071 this.createCookie(name,"",-1); 3072 }, 3073 3074 unload: function() 3075 { 3076 if (Shout.formmodified) 3077 { 3078 Shout.formmodified = false; 3079 } 3080 else 3081 { 3082 Shout.showLoadIndicator('#sui_content_container', 'Submitting', 0); 3083 } 3084 }, 3085 3086 animatePulse: function($el, duration, start_css, end_css, times) 3087 { 3088 $el.animate( 3089 end_css, 3090 duration, 3091 'linear', 3092 function() { 3093 if (times > 0) 3094 { 3095 Shout.animatePulse($el, duration, end_css, start_css, --times); 3096 } 3097 } 3098 ); 3099 }, 3100 3101 loadStart: function(query, msg) { 3102 3103// if (!Shout.progressTimer) 3104// { 3105// Shout.progressTimer = new Array(); 3106// } 3107 3108// Shout.progressTimer[query] = setTimeout(function(){ 3109 Shout.showUIBlock(query); 3110// }); 3111 3112 Shout.showLoadIndicator(query, msg, 0); 3113 3114 $(window).on('load', 3115 function (event) 3116 { 3117 $(document).on('shoutReady', function () 3118 { 3119 Shout.updateLoadIndicator(query, 'Finished', 100); 3120 Shout.hideLoadIndicator(query); 3121 Shout.hideUIBlock(query); 3122 } 3123 ); 3124 3125 Shout.isShoutReadyCheck(); 3126 } 3127 ); 3128 }, 3129 3130 isShoutReadyCheck: function () 3131 { 3132 var poll = 100; 3133 3134 if (!Shout.shoutReady) 3135 { 3136 if (typeof(Wysiwyg_Editor) != 'undefined' 3137 && Wysiwyg_Editor_Manager.editors()) 3138 { 3139 Shout.shoutReady = Wysiwyg_Editor_Manager.loaded(); 3140 } 3141 else 3142 { 3143 Shout.shoutReady = true; 3144 } 3145 3146 if (!Shout.shoutReady) 3147 { 3148 setTimeout(function(){ 3149 Shout.isShoutReadyCheck(); 3150 }, poll); 3151 3152 return false;
3153 } 3154 3155 setTimeout(function(){ 3156 Shout.shoutReady = true; 3157 $(document).trigger('shoutReady'); 3158 }, poll); 3159 3160 return false; 3161 } 3162 3163 return true; 3164 }, 3165 3166 loadProgress: function(query, msg, progress) { 3167 Shout.updateLoadIndicator(query, msg, progress); 3168 }, 3169 3170 loadComplete: function(query) { 3171// if (!Shout.progressTimer) 3172// { 3173// Shout.progressTimer = new Array(); 3174// } 3175// clearTimeout(Shout.progressTimer[query]); 3176 3177 Shout.updateLoadIndicator(query, 'Loading Additional Resources', 75); 3178 }, 3179 3180 modifiedFormListener: function(selector) 3181 { 3182 var strategy = 'serializeArray'; // order does not matter 3183 3184 if (typeof window.URLSearchParams !== 'undefined' 3185 && typeof window.FormData !== 'undefined') 3186 { 3187 var strategy = 'URLSearchParamsString'; // modern js but order does matter 3188 } 3189 3190 var equals = function(x, y) 3191 { 3192 var equals = true; 3193 3194 $(x).each( 3195 function() 3196 { 3197 var found = false; 3198 3199 var self = this; 3200 3201 // workaround for the wierd gallery init delay 3202 if (self.name === 'uploader_count') 3203 { 3204 return false; 3205 } 3206 3207 if (self.name === '_token') 3208 { 3209 return false; 3210 } 3211 3212 $(y).each( 3213 function() 3214 { 3215 if (this.name === self.name) 3216 { 3217 if (this.value === self.value) 3218 { 3219 found = true; 3220 return false; 3221 } 3222 } 3223 } 3224 ); 3225 3226 if (false === found) 3227 { 3228 equals = false; 3229 return false; 3230 } 3231 } 3232 ); 3233 3234 $(y).each( 3235 function() 3236 { 3237 var found = false; 3238 3239 var self = this; 3240 3241 // workaround for the wierd gallery init delay 3242 if (self.name === 'uploader_count') 3243 { 3244 return; 3245 } 3246 3247 if (self.name === '_token') 3248 { 3249 return; 3250 } 3251 3252 $(x).each( 3253 function() 3254 { 3255 if (this.name === self.name) 3256 { 3257 if (this.value === self.value) 3258 { 3259 found = true; 3260 return false; 3261 } 3262 } 3263 } 3264 ); 3265 3266 if (false === found) 3267 { 3268 equals = false; 3269 return false; 3270 } 3271 } 3272 ); 3273 3274 return equals; 3275 }; 3276 3277 var getFormData = function(form){ 3278 var formData = new FormData(form); 3279 formData.delete('_token'); 3280 formData.delete('uploader_count'); 3281 3282 return formData; 3283 }; 3284 3285 var getFormDataString = function(form){ 3286 var formData = getFormData(form); 3287 var urLSearchParams = new URLSearchParams(formData); 3288 var form_data_string = urLSearchParams.toString(); 3289 3290 return form_data_string; 3291 }; 3292 3293 var detectFormChanges = function(e) 3294 { 3295 var changed = false; 3296 3297 if (window.Wysiwyg_Editor_Manager) 3298 { 3299 Wysiwyg_Editor_Manager.refresh(); 3300 } 3301 3302 $forms = e.data.forms; 3303 3304 $forms.each( 3305 function() 3306 { 3307 $form = $(this); 3308 3309 if ('serialize' === strategy) 3310 { 3311 var currentFormData = $form.serialize(); 3312 var initialFormData = $form.data('initialFormData'); 3313 3314 if (currentFormData != initialFormData) 3315 { 3316 changed = true; 3317 } 3318 } 3319 else if ('serializeArray' === strategy) 3320 { 3321 var currentFormData = $form.serializeArray(); 3322 var initialFormData = $form.data('initialFormData'); 3323 3324 // console.group(); 3325 // console.log($form.prop('id')); 3326 // console.log(_.filter(currentFormData, function(v){return v.name === 'core[basics][title]';})); 3327 // console.log(_.filter(initialFormData, function(v){return v.name === 'core[basics][title]';})); 3328 // console.groupEnd(); 3329 3330 if (!equals(currentFormData, initialFormData)) 3331 { 3332 changed = true; 3333 } 3334 } 3335 else if ('URLSearchParamsString' === strategy) 3336 { 3337 var currentFormData = getFormDataString(this); 3338 var initialFormData = $form.data('initialFormData'); 3339 3340 if (currentFormData != initialFormData) 3341 { 3342 changed = true; 3343 } 3344 } 3345 else if ('URLSearchParams' === strategy) 3346 { 3347 throw 'not implemented'; 3348 } 3349 else if ('FormData' === strategy) 3350 { 3351 throw 'not implemented'; 3352 } 3353 else 3354 {
3355 throw 'invalid modified form detection strategy'; 3356 } 3357 } 3358 ); 3359 3360 if (changed) 3361 { 3362 /*e.stopPropagation();*/ 3363 3364 console.log('Unsaved Changes'); 3365 3366 Shout.formmodified = true; 3367 3368 setTimeout(function(){Shout.hideUIBlock();}, 1000); 3369 Shout.hideUIBlock(); 3370 3371 return 'Warning: You have unsaved changes on this page.'; 3372 } 3373 else 3374 { 3375 console.log('No Changes'); 3376 } 3377 }; 3378 3379 $(document).on('shoutReady', 3380 function () 3381 { 3382 console.log('shoutReady'); 3383 console.timeStamp('shoutReady'); 3384 3385 var $forms = $('form'+selector); 3386 3387 $forms.each( 3388 function() 3389 { 3390 var $form = $(this); 3391 if ('serialize' === strategy) 3392 { 3393 $form.data('initialFormData', $form.serialize()); 3394 } 3395 else if ('serializeArray' === strategy) 3396 { 3397 $form.data('initialFormData', $form.serializeArray()); 3398 } 3399 else if ('URLSearchParamsString' === strategy) 3400 { 3401 if (typeof window.URLSearchParams !== 'undefined' 3402 && typeof window.FormData !== 'undefined') 3403 { 3404 $form.data('initialFormData', getFormDataString(this)); 3405 } 3406 } 3407 else if ('URLSearchParams' === strategy) 3408 { 3409 $form.data('initialFormData', new URLSearchParams(getFormData(this))); 3410 } 3411 else if ('FormData' === strategy) 3412 { 3413 if (typeof window.URLSearchParams !== 'undefined' 3414 && typeof window.FormData !== 'undefined') 3415 { 3416 $form.data('initialFormData', tFormData(this)); 3417 } 3418 } 3419 else 3420 { 3421 throw 'invalid modified form detection strategy'; 3422 } 3423 } 3424 ).submit( 3425 function() 3426 { 3427 $(window).off('beforeunload', detectFormChanges); 3428 3429 window.setTimeout( 3430 function () 3431 { 3432 $(window).on('beforeunload', {forms: $forms}, detectFormChanges); 3433 } 3434 , 2000); 3435 } 3436 ); 3437 3438 $(window).on('beforeunload', {forms: $forms}, detectFormChanges); 3439 } 3440 ); 3441 }, 3442 3443 removeModifiedRankListener: function() 3444 { 3445 $(window).off('beforeunload.rankmodifiedlistener', detectRankChanges); 3446 }, 3447 3448 // works well for simple ranking forms, it does not handle ajax page loads well though since they do not trigger beforeunload 3449 addModifiedRankListener: function(selector) 3450 { 3451 var isRankChanged = function(current_ranks, original_ranks) 3452 { 3453 var changed = false; 3454 3455 var changed = !_.isEqual(original_ranks, current_ranks); 3456 //var changed = !original_ranks.is(current_ranks); 3457 3458 if (changed) 3459 { 3460 return 'You have unsaved rank changes on this page.'; 3461 } 3462 }; 3463 3464 var detectRankChanges = function(e) 3465 { 3466 var original_ranks = e.data.original_ranks; 3467 var selector = e.data.selector; 3468 var current_ranks = $(selector); 3469 3470 return isRankChanged(current_ranks, original_ranks); 3471 }; 3472 3473 var original_ranks = $(selector); 3474 $(window).on('beforeunload.rankmodifiedlistener', {selector: selector, original_ranks: original_ranks}, detectRankChanges); 3475 }, 3476 3477 // http://stackoverflow.com/questions/5069464/javascript-string-replace-array 3478 str_replace: function(find, replace, replaceString) 3479 { 3480 for (var i = 0; i < find.length; i++) { 3481 replaceString = replaceString.replace(find[i], replace[i]); 3482 } 3483 return replaceString; 3484 }, 3485
3486 // http://thinkclay.com/technology/add-edit-delete-cookies-with-javascript 3487 createCookie: function(name, value, minutes) { 3488 var expires = ''; 3489 if (minutes) 3490 { 3491 var date = new Date(); 3492 date.setTime(date.getTime() + (minutes * 60 * 1000)); 3493 expires = "; expires="+date.toGMTString(); 3494 } 3495 else 3496 { 3497 expires = ""; 3498 } 3499 3500 document.cookie = name+"="+value+expires; 3501 }, 3502 3503 eraseCookie: function(name) { 3504 var self = this; 3505 3506 self.createCookie(name, null, -1); 3507 }, 3508 3509// helpers: {}, 3510 helpers: ((typeof window.ShoutConfiguration != 'undefined') ? window.ShoutConfiguration.helpers : {}), 3511 3512 common: { 3513 3514 tooltipify: function() 3515 { 3516 _tooltipify(); 3517 }, 3518 3519 escapeQuote: function(selector, type) 3520 { 3521 var escaped_selector; 3522 3523 if ("undefined" === typeof type) 3524 { 3525 type = 'double'; 3526 } 3527 3528 if ('double' === type) 3529 { 3530 escaped_selector = selector.replace(/["]/g, "\\$&"); 3531 } 3532 else if ('single' === type) 3533 { 3534 escaped_selector = selector.replace(/[']/g, "\\$&"); 3535 } 3536 else 3537 { 3538 throw 'invalid type'; 3539 } 3540 3541 return escaped_selector; 3542 }, 3543 escapeCssSelector: function(selector) 3544 { 3545 var escaped_selector; 3546 3547 if ($.escapeSelector) 3548 { 3549 escaped_selector = $.escapeSelector(selector); 3550 } 3551 else if (window.CSS && window.CSS.escape) 3552 { 3553 escaped_selector = CSS.escape(selector); 3554 } 3555 else 3556 { 3557 escaped_selector = selector.replace(/[#;?%&,.+*~\':"!^$[\]()=>|\/@]/g, "\\\\$&"); 3558 } 3559 3560 return escaped_selector; 3561 }, 3562 checkBoxToggle: function(control, target, prop) 3563 { 3564 if ("undefined" === typeof prop) 3565 { 3566 prop = true; 3567 } 3568 3569 if (prop === $(control).prop('checked')) 3570 { 3571 $(target).show(); 3572 Shout.common.enableFormControls(target); 3573 } 3574 else 3575 { 3576 $(target).hide(); 3577 Shout.common.disableFormControls(target); 3578 } 3579 }, 3580 enableFormControlsOld: function(target) 3581 { 3582 $(target).find('input, select, textarea').each( 3583 function (key, value) 3584 { 3585 if ("undefined" !== typeof $(value).data('disabled-controls-disabled-state')) 3586 { 3587 if (false === $(value).data('disabled-controls-disabled-state')) 3588 { 3589 $(value).prop('disabled', false); 3590 } 3591 3592 $(value).removeData('disabled-controls-disabled-state'); 3593 } 3594 } 3595 ); 3596 }, 3597 disableFormControlsOld: function(target) 3598 { 3599 $(target).find('input, select, textarea').each( 3600 function (key, value) 3601 { 3602 if ("undefined" === typeof $(value).data('disabled-controls-disabled-state')) 3603 { 3604 $(value).data('disabled-controls-disabled-state', $(value).prop('disabled')); 3605 } 3606 3607 $(value).prop('disabled', true); 3608 } 3609 ); 3610 }, 3611 3612 enableFormControls: function(target, memento, force) 3613 { 3614 var debug = false; 3615 3616 Shout.common.enableFormControlsMemento(target, memento, force); 3617 3618 if (debug) 3619 { 3620 Shout.common.enableFormControls_redrawCounts(target); 3621 console.log('enableFormControls', {memento: memento, force:force, target:target}, new Error()); 3622 } 3623 }, 3624 3625 disableFormControls: function(target, memento, force) 3626 { 3627 var debug = false;
3628 3629 Shout.common.disableFormControlsMemento(target, memento, force); 3630 3631 if (debug) 3632 { 3633 Shout.common.enableFormControls_redrawCounts(target); 3634 console.log('disableFormControls', {memento: memento, force:force, target:target}, new Error()); 3635 } 3636 }, 3637 3638 enableFormControls_2017_12_21: function(target, remember, force) 3639 { 3640 if ("undefined" === typeof remember) 3641 { 3642 remember = true; 3643 } 3644 3645 if ("undefined" === typeof force) 3646 { 3647 force = false; 3648 } 3649 3650 $(target).find('input, select, textarea').each( 3651 function (key, value) 3652 { 3653 if (true === remember) 3654 { 3655 if ("undefined" === typeof $(value).data('disabled-controls-enabled-state')) 3656 { 3657 $(value).data('disabled-controls-enabled-state', !$(value).prop('disabled')); 3658 } 3659 } 3660 3661 if ("undefined" !== typeof $(value).data('disabled-controls-disabled-state')) 3662 { 3663 if (true === force 3664 || false === $(value).data('disabled-controls-disabled-state')) 3665 { 3666 $(value).prop('disabled', false); 3667 } 3668 3669 $(value).removeData('disabled-controls-disabled-state'); 3670 } 3671 else 3672 { 3673 $(value).prop('disabled', false); 3674 } 3675 } 3676 ); 3677 }, 3678 disableFormControls_2017_12_21: function(target, remember, force) 3679 { 3680 if ("undefined" === typeof remember) 3681 { 3682 remember = true; 3683 } 3684 3685 if ("undefined" === typeof force) 3686 { 3687 force = false; 3688 } 3689 3690 $(target).find('input, select, textarea').each( 3691 function (key, value) 3692 { 3693 if (true === remember) 3694 { 3695 if ("undefined" === typeof $(value).data('disabled-controls-disabled-state')) 3696 { 3697 $(value).data('disabled-controls-disabled-state', $(value).prop('disabled')); 3698 } 3699 } 3700 3701 if ("undefined" !== typeof $(value).data('disabled-controls-enabled-state')) 3702 { 3703 if (true === force 3704 || false === $(value).data('disabled-controls-enabled-state')) 3705 { 3706 $(value).prop('disabled', true); 3707 } 3708 3709 $(value).removeData('disabled-controls-enabled-state'); 3710 } 3711 else 3712 { 3713 $(value).prop('disabled', true); 3714 } 3715 } 3716 ); 3717 }, 3718 3719 _ensureFormControlsMementoInitialized: function(target) 3720 { 3721 $(target).find('input, select, textarea').add(target).each( 3722 function (key, value) 3723 { 3724 var state = $(value).data('disabled-controls-state'); 3725 3726 if ("undefined" === typeof state) 3727 { 3728 $parent = $(value).closest('*[data-disabled-controls-state]'); 3729 3730 if ($parent.length > 0) 3731 { 3732 state = Object.assign({}, $parent.data('disabled-controls-state')); 3733 } 3734 else 3735 { 3736 state = {}; 3737 } 3738 3739 $(value).data('disabled-controls-state', state); 3740 } 3741 } 3742 ); 3743 }, 3744 3745 /** 3746 * Each formControl store a history of what intent has applied disabled state on it and also 3747 * the state of the control before the disabled state was applied. 3748 * @param target: the form container 3749 * @param memento: the intent (e.g. the checkbox) that want to enableFormControls 3750 * @param force: force the form to restore to initial state no matter if other intents has applied disable on the form 3751 */ 3752 enableFormControlsMemento: function(target, memento, force) 3753 { 3754 var isDebug = false;
3755 3756 if ("undefined" === typeof memento) 3757 { 3758 memento = 'default'; 3759 } 3760 3761 if ("undefined" === typeof force) 3762 { 3763 force = false; 3764 } 3765 3766 Shout.common._ensureFormControlsMementoInitialized(target); 3767 3768 $(target).find('input, select, textarea, *[data-disabled-controls-state]').add(target).each( 3769 function (key, value) 3770 { 3771 if ($(value).is(target)) 3772 { 3773 // only process enable/disableFormControl on the target element itself 3774 // if it has the disabled-controls-state data 3775 if ('undefined' == typeof $(value).data('disabled-controls-state')) 3776 { 3777 return; 3778 } 3779 } 3780 3781 if (force) 3782 { 3783 Shout.common.setControlDisabled($(value), false); 3784 } 3785 else 3786 { 3787 if (isDebug) 3788 { 3789 console.log('---- BEGIN ----', value, 'disabled = ', $(value).prop('disabled')); 3790 } 3791 3792 // retrieve the saved disabled-state of the control 3793 var state = $(value).data('disabled-controls-state'); 3794 if ("undefined" === typeof state) 3795 { 3796 state = {}; 3797 } 3798 3799 var disabled = state[memento]; 3800 3801 var initialsize = _.size(state); //brilliant!!!, this is to prevent the enableFormControlsMemento from being called many time on an already enabled control 3802 3803 delete state[memento]; 3804 3805 $(value).data('disabled-controls-state', state); 3806 3807 // if all intents have called enableFormControls on this control 3808 // restore its original state 3809 if (0 === _.size(state) && 0 < initialsize) // test memento?? 3810 { 3811 Shout.common.setControlDisabled($(value), disabled === true); 3812 } 3813 3814 if (isDebug) 3815 { 3816 console.log('disabled = ', disabled, 'initialsize = ', initialsize); 3817 3818 console.log('---- END ----', value, 'disabled = ', $(value).prop('disabled')); 3819 } 3820 } 3821 } 3822 ); 3823 }, 3824 3825 /** 3826 * Each formControl store a history of what intent has applied disabled state on it and also 3827 * the state of the control before the disabled state was applied. 3828 */ 3829 disableFormControlsMemento: function(target, memento, force) 3830 { 3831 var isDebug = false; 3832 3833 if ("undefined" === typeof memento) 3834 { 3835 memento = 'default'; 3836 } 3837 3838 if ("undefined" === typeof force) 3839 { 3840 force = false; 3841 } 3842 3843 var state = null; 3844 var newstate = null; 3845 3846 Shout.common._ensureFormControlsMementoInitialized(target); 3847 3848 $(target).find('input, select, textarea, *[data-disabled-controls-state]').add(target).each( 3849 function (key, value) 3850 { 3851 if ($(value).is(target)) 3852 { 3853 // only process enable/disableFormControl on the target element itself 3854 // if it has the disabled-controls-state data 3855 if ('undefined' == typeof $(value).data('disabled-controls-state')) 3856 { 3857 return; 3858 } 3859 } 3860 3861 if (force) 3862 { 3863 Shout.common.setControlDisabled($(value), true); 3864 } 3865 else 3866 { 3867 // save the disabled-state of the control with regard to memento 3868 if ("undefined" === typeof $(value).data('disabled-controls-state')) 3869 { 3870 state = {}; 3871 $(value).data('disabled-controls-state', state); 3872 } 3873 else 3874 { 3875 state = $(value).data('disabled-controls-state'); 3876 } 3877 3878 if ("undefined" === typeof state[memento]) 3879 { 3880 newstate = {}; 3881 newstate[memento] = $(value).prop('disabled'); 3882 3883 $.extend(state, newstate); 3884 $(value).data('disabled-controls-state', state); 3885 } 3886 3887 // disable the control 3888 Shout.common.setControlDisabled($(value), true); 3889 } 3890 } 3891 ); 3892 }, 3893 3894 enableFormControls_redrawCounts: function(target, propName) 3895 { 3896 jQuery(target).find('input,*[data-disabled-controls-state]').add(jQuery('.toggle-block')).add(target).each(function(key, value){ 3897 var elm = jQuery(value); 3898 var sib = elm.next(); 3899 if (!sib.hasClass('count')){ 3900 sib = jQuery('<div style="display:inline-block;white-space:pre;font-size:10px;vertical-align:middle" class="count"><div>'); 3901 elm.after(sib); 3902 } 3903 3904 var data; 3905 if (propName) { 3906 data = elm.data(propName); 3907 } 3908 else { 3909 data = { 3910 initial: elm.data('initial-disabled-controls-state'), 3911 current: elm.data('disabled-controls-state') 3912 }; 3913 //elm.data() 3914 } 3915 var text = '#data contains circular references#'; 3916 try{ 3917 text = ('(' + JSON.stringify(data, null, 4) + ')'); 3918 } 3919 catch(reason){} 3920 sib.text( text ); 3921 }); 3922 }, 3923 3924 /** 3925 * It would be ideal if all jQuery ui widgets can be enabled/disabled by just calling prop('disabled') 3926 * on the host input field. However, it is not possible without subclassing the widgets. 3927 */ 3928 setControlDisabled: function($controls, disabled) 3929 { 3930 $controls.each(function() 3931 { 3932 var $control = $(this); 3933 3934 var widgetDefs = { 3935 'ui-selectmenu' : 'selectmenu' 3936 }; 3937 3938 $control.prop('disabled', disabled); 3939 3940 // if the control is a jquery ui widget, 3941 // set its disabled option accordingly 3942 for (var jquery_ui_widget_data_key in widgetDefs) 3943 { 3944 if ('object' === typeof $control.data(jquery_ui_widget_data_key)) 3945 { 3946 var jquery_ui_widget_name = widgetDefs[jquery_ui_widget_data_key]; 3947 $control[jquery_ui_widget_name]('option', 'disabled', disabled); 3948 } 3949 } 3950 } 3951 ); 3952 }, 3953 3954 // initialize a checkbox (parentCheckbox) that automatically check/uncheck other checkboxes (childCheckbox) 3955 // TODO: make this dynamic and attach click handlers to window rather i.e. window.on('click', selector, handler)... 3956 // see code/core/src/Core/AddressBook/Display/AddressBook/AbstractGridDisplay.php 3957 // see regex \.(on|bind)\(\s*['"][a-zA-Z0-9 ]+['"],\s*['"][^'"]+['"]\s*, 3958 initSelectAllCheckbox: function($parentCheckbox, $childCheckboxes) 3959 { 3960 $parentCheckbox = $($parentCheckbox); 3961 $childCheckboxes = $($childCheckboxes); 3962 3963 $parentCheckbox.on('click', 3964 function(e) 3965 { 3966 var self = e.target; 3967 var $self = $(e.target); 3968 3969 var is_checked = $self.is(':checked'); 3970 3971 $childCheckboxes.prop('checked', is_checked); 3972 $parentCheckbox.prop('checked', is_checked); 3973 } 3974 ); 3975 $childCheckboxes.on('click', 3976 function(e) 3977 { 3978 var selected = ($childCheckboxes.length == $childCheckboxes.filter(':checked').length); 3979 3980 $parentCheckbox.prop('checked', selected); 3981 } 3982 ); 3983 } 3984 }, 3985 3986 openPopUp: function(url, width, height) 3987 { 3988 empty = window.open(url, 'popup', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width='+width+',height='+height); 3989 }, 3990 3991 range: { 3992 3993 _params: { 3994 range_separator: '-', 3995 separator: ',', 3996 sort: true, 3997 uniq: true 3998 }, 3999 4000 _isIntergerish: function(value) 4001 { 4002 if (typeof value === 'string') 4003 { 4004 if ('' === value.trim()) 4005 { 4006 return false;
4007 } 4008 } 4009 4010 if (isNaN(value)) 4011 { 4012 return false; 4013 } 4014 4015 var num = Number(value); 4016 if (!Number.isInteger(num)) 4017 { 4018 return false; 4019 } 4020 4021 return true; 4022 }, 4023 4024 extractIntRange: function(expanded_range) 4025 { 4026 var self = this; 4027 4028 var range_separator = self._params.range_separator; 4029 var separator = self._params.separator; 4030 4031 _.forEach(expanded_range, function(value){ 4032 if (!self._isIntergerish(value)) 4033 { 4034 throw 'range item is not integerish: `'+value+'`'; 4035 } 4036 }); 4037 4038 expanded_range = _.map(expanded_range, function(value) { 4039 return _.parseInt(value); 4040 }); 4041 4042 if (self._params.sort) 4043 { 4044 expanded_range = expanded_range.sort(function(a,b) { 4045 return a - b; 4046 }); 4047 } 4048 4049 if (self._params.uniq) 4050 { 4051 expanded_range = _.sortedUniq(expanded_range); 4052 } 4053 4054 var len = expanded_range.length; 4055 4056 var i = 0; 4057 var j = 0; 4058 4059 var out = new Array(); 4060 4061 for (i = 0; i < len; i = j + 1) 4062 { 4063 // beginning of range or single 4064 out.push(expanded_range[i]); 4065 4066 // find end of range 4067 for (j = (i + 1); (j < len) && ((expanded_range[j]) === ((expanded_range[j-1]) + 1)); j++); 4068 j--; 4069 4070 if (i === j) 4071 { 4072 // single number 4073 out.push(separator); 4074 } 4075 else if ((i + 1) === j) 4076 { 4077 // two numbers 4078 out.push(separator, (expanded_range[j]), separator); 4079 } 4080 else 4081 { 4082 // range 4083 out.push(range_separator, (expanded_range[j]), separator); 4084 } 4085 } 4086 4087 out.pop(); // remove trailing comma 4088 4089 return out.join(''); 4090 }, 4091 4092 expandIntRange: function(range) 4093 { 4094 var self = this; 4095 4096 var regex = new RegExp('^-?[0-9]+(--?[0-9]+)?(,-?[0-9]+(--?[0-9]+)?)*$'); 4097 if (false === regex.test(range)) 4098 { 4099 throw 'invalid range string: `'+range+'`'; 4100 } 4101 4102 var range_separator = self._params.range_separator; 4103 var separator = self._params.separator; 4104 4105 var lst = new Array(); 4106 4107 var ranges = range.split(separator); 4108 4109 _.forEach(ranges, function(subrange) { 4110 var pos = null; 4111 if (-1 === (pos = subrange.indexOf(range_separator, 1))) 4112 { 4113 lst.push(_.parseInt(subrange)); 4114 return; 4115 } 4116 4117 var a = _.parseInt(subrange.substr(0,pos)); 4118 var b = _.parseInt(subrange.substr(pos+1)); 4119 4120 for (var j = a; j <= b; j++) 4121 { 4122 lst.push(j); 4123 } 4124 }); 4125 4126 return lst; 4127 } 4128 } 4129}); 4130 4131// fix for console.log slipups 4132 4133if(window) 4134{ 4135 if (!window.console) 4136 { 4137 window.console = { log: function(){} }; 4138 } 4139 4140 if (typeof window.console.timeStamp !== 'function') 4141 { 4142 console.log('console.timeStamp is not supported in this browser!'); 4143 window.console.timeStamp = function() {}; 4144 } 4145 4146 if (typeof window.console.debug !== 'function') 4147 { 4148 console.log('console.debug is not natively supported in this browser!'); 4149 window.console.debug = function(msg) {console.log(msg);}; 4150 } 4151} 4152 4153})(jQuery, ((typeof SystemJS !== 'undefined') ? SystemJS : System), _);
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.