1/* 2 * File: iframeResizer.js 3 * Desc: Force iframes to size to content. 4 * Requires: iframeResizer.contentWindow.js to be loaded into the target frame. 5 * Doc: https://github.com/davidjbradshaw/iframe-resizer 6 * Author: David J. Bradshaw - [email protected] 7 * Contributor: Jure Mav - [email protected] 8 * Contributor: Reed Dadoune - [email protected] 9 */ 10 11// eslint-disable-next-line sonarjs/cognitive-complexity, no-shadow-restricted-names 12;(function (undefined) { 13 if (typeof window === 'undefined') return // don't run for server side render 14 15 var count = 0, 16 logEnabled = false, 17 hiddenCheckEnabled = false, 18 msgHeader = 'message', 19 msgHeaderLen = msgHeader.length, 20 msgId = '[iFrameSizer]', // Must match iframe msg ID 21 msgIdLen = msgId.length, 22 pagePosition = null, 23 requestAnimationFrame = window.requestAnimationFrame, 24 resetRequiredMethods = { 25 max: 1, 26 scroll: 1, 27 bodyScroll: 1, 28 documentElementScroll: 1 29 }, 30 settings = {}, 31 timer = null, 32 defaults = { 33 autoResize: true, 34 bodyBackground: null, 35 bodyMargin: null, 36 bodyMarginV1: 8, 37 bodyPadding: null, 38 checkOrigin: true, 39 inPageLinks: false, 40 enablePublicMethods: true, 41 heightCalculationMethod: 'bodyOffset', 42 id: 'iFrameResizer', 43 interval: 32, 44 log: false, 45 maxHeight: Infinity, 46 maxWidth: Infinity, 47 minHeight: 0, 48 minWidth: 0, 49 mouseEvents: true, 50 resizeFrom: 'parent', 51 scrolling: false, 52 sizeHeight: true, 53 sizeWidth: false, 54 warningTimeout: 5000, 55 tolerance: 0, 56 widthCalculationMethod: 'scroll', 57 onClose: function () { 58 return true 59 }, 60 onClosed: function () {}, 61 onInit: function () {}, 62 onMessage: function () { 63 warn('onMessage function not defined') 64 }, 65 onMouseEnter: function () {}, 66 onMouseLeave: function () {}, 67 onResized: function () {}, 68 onScroll: function () { 69 return true 70 } 71 } 72 73 function getMutationObserver() { 74 return ( 75 window.MutationObserver || 76 window.WebKitMutationObserver || 77 window.MozMutationObserver 78 ) 79 } 80 81 function addEventListener(el, evt, func) { 82 el.addEventListener(evt, func, false) 83 } 84 85 function removeEventListener(el, evt, func) { 86 el.removeEventListener(evt, func, false) 87 } 88 89 function setupRequestAnimationFrame() { 90 var vendors = ['moz', 'webkit', 'o', 'ms'] 91 var x 92 93 // Remove vendor prefixing if prefixed and break early if not 94 for (x = 0; x < vendors.length && !requestAnimationFrame; x += 1) { 95 requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'] 96 } 97 98 if (!requestAnimationFrame) { 99 log('setup', 'RequestAnimationFrame not supported') 100 } else { 101 // Firefox extension content-scripts have a globalThis object that is not the same as window. 102 // Binding `requestAnimationFrame` to window allows the function to work and prevents errors 103 // being thrown when run in that context, and should be a no-op in every other context. 104 requestAnimationFrame = requestAnimationFrame.bind(window) 105 } 106 } 107 108 function getMyID(iframeId) { 109 var retStr = 'Host page: ' + iframeId 110 111 if (window.top !== window.self) { 112 retStr = 113 window.parentIFrame && window.parentIFrame.getId 114 ? window.parentIFrame.getId() + ': ' + iframeId 115 : 'Nested host page: ' + iframeId 116 } 117 118 return retStr 119 } 120 121 function formatLogHeader(iframeId) { 122 return msgId + '[' + getMyID(iframeId) + ']' 123 } 124 125 function isLogEnabled(iframeId) { 126 return settings[iframeId] ? settings[iframeId].log : logEnabled 127 } 128 129 function log(iframeId, msg) { 130 output('log', iframeId, msg, isLogEnabled(iframeId)) 131 } 132 133 function info(iframeId, msg) { 134 output('info', iframeId, msg, isLogEnabled(iframeId)) 135 } 136 137 function warn(iframeId, msg) { 138 output('warn', iframeId, msg, true) 139 } 140 141 function output(type, iframeId, msg, enabled) { 142 if (true === enabled && 'object' === typeof window.console) { 143 // eslint-disable-next-line no-console 144 console[type](formatLogHeader(iframeId), msg) 145 } 146 } 147 148 function iFrameListener(event) { 149 function resizeIFrame() { 150 function resize() { 151 setSize(messageData) 152 setPagePosition(iframeId) 153 on('onResized', messageData) 154 } 155 156 ensureInRange('Height') 157 ensureInRange('Width') 158 159 syncResize(resize, messageData, 'init') 160 } 161 162 function processMsg() { 163 var data = msg.substr(msgIdLen).split(':') 164 var height = data[1] ? parseInt(data[1], 10) : 0 165 var iframe = settings[data[0]] && settings[data[0]].iframe 166 var compStyle = getComputedStyle(iframe) 167 168 return { 169 iframe: iframe, 170 id: data[0], 171 height: height + getPaddingEnds(compStyle) + getBorderEnds(compStyle), 172 width: data[2], 173 type: data[3] 174 } 175 } 176 177 function getPaddingEnds(compStyle) { 178 if (compStyle.boxSizing !== 'border-box') { 179 return 0 180 } 181 var top = compStyle.paddingTop ? parseInt(compStyle.paddingTop, 10) : 0 182 var bot = compStyle.paddingBottom 183 ? parseInt(compStyle.paddingBottom, 10) 184 : 0 185 return top + bot 186 } 187 188 function getBorderEnds(compStyle) { 189 if (compStyle.boxSizing !== 'border-box') { 190 return 0 191 } 192 var top = compStyle.borderTopWidth 193 ? parseInt(compStyle.borderTopWidth, 10) 194 : 0 195 var bot = compStyle.borderBottomWidth 196 ? parseInt(compStyle.borderBottomWidth, 10) 197 : 0 198 return top + bot 199 } 200 201 function ensureInRange(Dimension) { 202 var max = Number(settings[iframeId]['max' + Dimension]), 203 min = Number(settings[iframeId]['min' + Dimension]), 204 dimension = Dimension.toLowerCase(), 205 size = Number(messageData[dimension]) 206 207 log(iframeId, 'Checking ' + dimension + ' is in range ' + min + '-' + max) 208 209 if (size < min) { 210 size = min 211 log(iframeId, 'Set ' + dimension + ' to min value') 212 } 213 214 if (size > max) { 215 size = max 216 log(iframeId, 'Set ' + dimension + ' to max value') 217 } 218 219 messageData[dimension] = '' + size 220 } 221 222 function isMessageFromIFrame() { 223 function checkAllowedOrigin() { 224 function checkList() { 225 var i = 0, 226 retCode = false 227 228 log( 229 iframeId, 230 'Checking connection is from allowed list of origins: ' + 231 checkOrigin 232 ) 233 234 for (; i < checkOrigin.length; i++) { 235 if (checkOrigin[i] === origin) { 236 retCode = true 237 break 238 } 239 } 240 return retCode 241 } 242 243 function checkSingle() { 244 var remoteHost = settings[iframeId] && settings[iframeId].remoteHost 245 log(iframeId, 'Checking connection is from: ' + remoteHost) 246 return origin === remoteHost 247 } 248 249 return checkOrigin.constructor === Array ? checkList() : checkSingle() 250 } 251 252 var origin = event.origin, 253 checkOrigin = settings[iframeId] && settings[iframeId].checkOrigin 254 255 if (checkOrigin && '' + origin !== 'null' && !checkAllowedOrigin()) { 256 throw new Error( 257 'Unexpected message received from: ' + 258 origin + 259 ' for ' + 260 messageData.iframe.id + 261 '. Message was: ' + 262 event.data + 263 '. This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.' 264 ) 265 } 266 267 return true 268 } 269 270 function isMessageForUs() { 271 return ( 272 msgId === ('' + msg).substr(0, msgIdLen) && 273 msg.substr(msgIdLen).split(':')[0] in settings 274 ) // ''+Protects against non-string msg 275 } 276 277 function isMessageFromMetaParent() { 278 // Test if this message is from a parent above us. This is an ugly test, however, updating 279 // the message format would break backwards compatibility. 280 var retCode = messageData.type in { true: 1, false: 1, undefined: 1 } 281 282 if (retCode) { 283 log(iframeId, 'Ignoring init message from meta parent page') 284 } 285 286 return retCode 287 } 288 289 function getMsgBody(offset) { 290 return msg.substr(msg.indexOf(':') + msgHeaderLen + offset) 291 } 292 293 function forwardMsgFromIFrame(msgBody) { 294 log( 295 iframeId, 296 'onMessage passed: {iframe: ' + 297 messageData.iframe.id + 298 ', message: ' + 299 msgBody + 300 '}' 301 ) 302 303 on('onMessage', { 304 iframe: messageData.iframe, 305 message: JSON.parse(msgBody) 306 }) 307
308 log(iframeId, '--') 309 } 310 311 function getPageInfo() { 312 var bodyPosition = document.body.getBoundingClientRect(), 313 iFramePosition = messageData.iframe.getBoundingClientRect() 314 315 return JSON.stringify({ 316 iframeHeight: iFramePosition.height, 317 iframeWidth: iFramePosition.width, 318 clientHeight: Math.max( 319 document.documentElement.clientHeight, 320 window.innerHeight || 0 321 ), 322 clientWidth: Math.max( 323 document.documentElement.clientWidth, 324 window.innerWidth || 0 325 ), 326 offsetTop: parseInt(iFramePosition.top - bodyPosition.top, 10), 327 offsetLeft: parseInt(iFramePosition.left - bodyPosition.left, 10), 328 scrollTop: window.pageYOffset, 329 scrollLeft: window.pageXOffset, 330 documentHeight: document.documentElement.clientHeight, 331 documentWidth: document.documentElement.clientWidth, 332 windowHeight: window.innerHeight, 333 windowWidth: window.innerWidth 334 }) 335 } 336 337 function sendPageInfoToIframe(iframe, iframeId) { 338 function debouncedTrigger() { 339 trigger('Send Page Info', 'pageInfo:' + getPageInfo(), iframe, iframeId) 340 } 341 debounceFrameEvents(debouncedTrigger, 32, iframeId) 342 } 343 344 function startPageInfoMonitor() { 345 function setListener(type, func) { 346 function sendPageInfo() { 347 if (settings[id]) { 348 sendPageInfoToIframe(settings[id].iframe, id) 349 } else { 350 stop() 351 } 352 } 353 354 ;['scroll', 'resize'].forEach(function (evt) { 355 log(id, type + evt + ' listener for sendPageInfo') 356 func(window, evt, sendPageInfo) 357 }) 358 } 359 360 function stop() { 361 setListener('Remove ', removeEventListener) 362 } 363 364 function start() { 365 setListener('Add ', addEventListener) 366 } 367 368 var id = iframeId // Create locally scoped copy of iFrame ID 369 370 start() 371 372 if (settings[id]) { 373 settings[id].stopPageInfo = stop 374 } 375 } 376 377 function stopPageInfoMonitor() { 378 if (settings[iframeId] && settings[iframeId].stopPageInfo) { 379 settings[iframeId].stopPageInfo() 380 delete settings[iframeId].stopPageInfo 381 } 382 } 383 384 function checkIFrameExists() { 385 var retBool = true 386 387 if (null === messageData.iframe) { 388 warn(iframeId, 'IFrame (' + messageData.id + ') not found') 389 retBool = false 390 } 391 return retBool 392 } 393 394 function getElementPosition(target) { 395 var iFramePosition = target.getBoundingClientRect() 396 397 getPagePosition(iframeId) 398 399 return { 400 x: Math.floor(Number(iFramePosition.left) + Number(pagePosition.x)), 401 y: Math.floor(Number(iFramePosition.top) + Number(pagePosition.y)) 402 } 403 } 404 405 function scrollRequestFromChild(addOffset) { 406 /* istanbul ignore next */ // Not testable in Karma 407 function reposition() { 408 pagePosition = newPosition 409 scrollTo() 410 log(iframeId, '--') 411 } 412 413 function calcOffset() { 414 return { 415 x: Number(messageData.width) + offset.x, 416 y: Number(messageData.height) + offset.y 417 } 418 } 419 420 function scrollParent() { 421 if (window.parentIFrame) { 422 window.parentIFrame['scrollTo' + (addOffset ? 'Offset' : '')]( 423 newPosition.x, 424 newPosition.y 425 ) 426 } else { 427 warn( 428 iframeId, 429 'Unable to scroll to requested position, window.parentIFrame not found' 430 ) 431 } 432 } 433 434 var offset = addOffset 435 ? getElementPosition(messageData.iframe) 436 : { x: 0, y: 0 }, 437 newPosition = calcOffset() 438 439 log( 440 iframeId, 441 'Reposition requested from iFrame (offset x:' + 442 offset.x + 443 ' y:' + 444 offset.y + 445 ')' 446 ) 447 448 if (window.top !== window.self) { 449 scrollParent() 450 } else { 451 reposition() 452 } 453 } 454 455 function scrollTo() { 456 if (false !== on('onScroll', pagePosition)) { 457 setPagePosition(iframeId) 458 } else { 459 unsetPagePosition() 460 } 461 } 462 463 function findTarget(location) { 464 function jumpToTarget() { 465 var jumpPosition = getElementPosition(target) 466 467 log( 468 iframeId, 469 'Moving to in page link (#' + 470 hash + 471 ') at x: ' + 472 jumpPosition.x + 473 ' y: ' + 474 jumpPosition.y 475 ) 476 pagePosition = { 477 x: jumpPosition.x, 478 y: jumpPosition.y 479 } 480 481 scrollTo()
482 log(iframeId, '--') 483 } 484 485 function jumpToParent() { 486 if (window.parentIFrame) { 487 window.parentIFrame.moveToAnchor(hash) 488 } else { 489 log( 490 iframeId, 491 'In page link #' + 492 hash + 493 ' not found and window.parentIFrame not found' 494 ) 495 } 496 } 497 498 var hash = location.split('#')[1] || '', 499 hashData = decodeURIComponent(hash), 500 target = 501 document.getElementById(hashData) || 502 document.getElementsByName(hashData)[0] 503 504 if (target) { 505 jumpToTarget() 506 } else if (window.top !== window.self) { 507 jumpToParent() 508 } else { 509 log(iframeId, 'In page link #' + hash + ' not found') 510 } 511 } 512 513 function onMouse(event) { 514 var mousePos = {} 515 516 if (Number(messageData.width) === 0 && Number(messageData.height) === 0) { 517 var data = getMsgBody(9).split(':') 518 mousePos = { 519 x: data[1], 520 y: data[0] 521 } 522 } else { 523 mousePos = { 524 x: messageData.width, 525 y: messageData.height 526 } 527 } 528 529 on(event, { 530 iframe: messageData.iframe, 531 screenX: Number(mousePos.x), 532 screenY: Number(mousePos.y), 533 type: messageData.type 534 }) 535 } 536 537 function on(funcName, val) { 538 return chkEvent(iframeId, funcName, val) 539 } 540 541 function actionMsg() { 542 if (settings[iframeId] && settings[iframeId].firstRun) firstRun() 543 544 switch (messageData.type) { 545 case 'close': 546 closeIFrame(messageData.iframe) 547 break 548 549 case 'message': 550 forwardMsgFromIFrame(getMsgBody(6)) 551 break 552 553 case 'mouseenter': 554 onMouse('onMouseEnter') 555 break 556 557 case 'mouseleave': 558 onMouse('onMouseLeave') 559 break 560 561 case 'autoResize': 562 settings[iframeId].autoResize = JSON.parse(getMsgBody(9)) 563 break 564 565 case 'scrollTo': 566 scrollRequestFromChild(false) 567 break 568 569 case 'scrollToOffset': 570 scrollRequestFromChild(true) 571 break 572 573 case 'pageInfo': 574 sendPageInfoToIframe( 575 settings[iframeId] && settings[iframeId].iframe, 576 iframeId 577 ) 578 startPageInfoMonitor() 579 break 580 581 case 'pageInfoStop': 582 stopPageInfoMonitor() 583 break 584 585 case 'inPageLink': 586 findTarget(getMsgBody(9)) 587 break 588 589 case 'reset': 590 resetIFrame(messageData) 591 break 592 593 case 'init': 594 resizeIFrame() 595 on('onInit', messageData.iframe) 596 break 597 598 default: 599 if ( 600 Number(messageData.width) === 0 && 601 Number(messageData.height) === 0 602 ) { 603 warn( 604 'Unsupported message received (' + 605 messageData.type + 606 '), this is likely due to the iframe containing a later ' + 607 'version of iframe-resizer than the parent page' 608 ) 609 } else { 610 resizeIFrame() 611 } 612 } 613 } 614 615 function hasSettings(iframeId) { 616 var retBool = true 617 618 if (!settings[iframeId]) { 619 retBool = false 620 warn( 621 messageData.type + 622 ' No settings for ' + 623 iframeId + 624 '. Message was: ' + 625 msg 626 ) 627 } 628 629 return retBool 630 } 631 632 function iFrameReadyMsgReceived() { 633 // eslint-disable-next-line no-restricted-syntax, guard-for-in 634 for (var iframeId in settings) { 635 trigger( 636 'iFrame requested init', 637 createOutgoingMsg(iframeId), 638 settings[iframeId].iframe, 639 iframeId 640 ) 641 } 642 } 643 644 function firstRun() { 645 if (settings[iframeId]) { 646 settings[iframeId].firstRun = false 647 } 648 } 649 650 var msg = event.data, 651 messageData = {}, 652 iframeId = null 653 654 if ('[iFrameResizerChild]Ready' === msg) { 655 iFrameReadyMsgReceived() 656 } else if (isMessageForUs()) { 657 messageData = processMsg() 658 iframeId = messageData.id 659 if (settings[iframeId]) { 660 settings[iframeId].loaded = true 661 } 662 663 if (!isMessageFromMetaParent() && hasSettings(iframeId)) { 664 log(iframeId, 'Received: ' + msg) 665 666 if (checkIFrameExists() && isMessageFromIFrame()) { 667 actionMsg() 668 } 669 } 670 } else { 671 info(iframeId, 'Ignored: ' + msg) 672 } 673 } 674 675 function chkEvent(iframeId, funcName, val) { 676 var func = null, 677 retVal = null 678 679 if (settings[iframeId]) { 680 func = settings[iframeId][funcName] 681 682 if ('function' === typeof func) { 683 retVal = func(val) 684 } else { 685 throw new TypeError( 686 funcName + ' on iFrame[' + iframeId + '] is not a function' 687 ) 688 } 689 } 690 691 return retVal 692 } 693 694 function removeIframeListeners(iframe) { 695 var iframeId = iframe.id 696 delete settings[iframeId] 697 } 698 699 function closeIFrame(iframe) { 700 var iframeId = iframe.id 701 if (chkEvent(iframeId, 'onClose', iframeId) === false) { 702 log(iframeId, 'Close iframe cancelled by onClose event') 703 return 704 } 705 log(iframeId, 'Removing iFrame: ' + iframeId) 706 707 try { 708 // Catch race condition error with React 709 if (iframe.parentNode) { 710 iframe.parentNode.removeChild(iframe) 711 } 712 } catch (error) { 713 warn(error) 714 } 715 716 chkEvent(iframeId, 'onClosed', iframeId)
717 log(iframeId, '--') 718 removeIframeListeners(iframe) 719 } 720 721 function getPagePosition(iframeId) { 722 if (null === pagePosition) { 723 pagePosition = { 724 x: 725 window.pageXOffset !== undefined 726 ? window.pageXOffset 727 : document.documentElement.scrollLeft, 728 y: 729 window.pageYOffset !== undefined 730 ? window.pageYOffset 731 : document.documentElement.scrollTop 732 } 733 log( 734 iframeId, 735 'Get page position: ' + pagePosition.x + ',' + pagePosition.y 736 ) 737 } 738 } 739 740 function setPagePosition(iframeId) { 741 if (null !== pagePosition) { 742 window.scrollTo(pagePosition.x, pagePosition.y) 743 log( 744 iframeId, 745 'Set page position: ' + pagePosition.x + ',' + pagePosition.y 746 ) 747 unsetPagePosition() 748 } 749 } 750 751 function unsetPagePosition() { 752 pagePosition = null 753 } 754 755 function resetIFrame(messageData) { 756 function reset() { 757 setSize(messageData) 758 trigger('reset', 'reset', messageData.iframe, messageData.id) 759 } 760 761 log( 762 messageData.id, 763 'Size reset requested by ' + 764 ('init' === messageData.type ? 'host page' : 'iFrame') 765 ) 766 getPagePosition(messageData.id) 767 syncResize(reset, messageData, 'reset') 768 } 769 770 function setSize(messageData) { 771 function setDimension(dimension) { 772 if (!messageData.id) { 773 log('undefined', 'messageData id not set') 774 return 775 } 776 messageData.iframe.style[dimension] = messageData[dimension] + 'px' 777 log( 778 messageData.id, 779 'IFrame (' + 780 iframeId + 781 ') ' + 782 dimension + 783 ' set to ' + 784 messageData[dimension] + 785 'px' 786 ) 787 } 788 789 function chkZero(dimension) { 790 // FireFox sets dimension of hidden iFrames to zero. 791 // So if we detect that set up an event to check for 792 // when iFrame becomes visible. 793 794 /* istanbul ignore next */ // Not testable in PhantomJS 795 if (!hiddenCheckEnabled && '0' === messageData[dimension]) { 796 hiddenCheckEnabled = true 797 log(iframeId, 'Hidden iFrame detected, creating visibility listener') 798 fixHiddenIFrames() 799 } 800 } 801 802 function processDimension(dimension) { 803 setDimension(dimension) 804 chkZero(dimension) 805 } 806 807 var iframeId = messageData.iframe.id 808 809 if (settings[iframeId]) { 810 if (settings[iframeId].sizeHeight) { 811 processDimension('height') 812 } 813 if (settings[iframeId].sizeWidth) { 814 processDimension('width') 815 } 816 } 817 } 818 819 function syncResize(func, messageData, doNotSync) { 820 /* istanbul ignore if */ // Not testable in PhantomJS 821 if ( 822 doNotSync !== messageData.type && 823 requestAnimationFrame && 824 // including check for jasmine because had trouble getting spy to work in unit test using requestAnimationFrame 825 !window.jasmine 826 ) { 827 log(messageData.id, 'Requesting animation frame') 828 requestAnimationFrame(func) 829 } else { 830 func() 831 } 832 } 833 834 function trigger(calleeMsg, msg, iframe, id, noResponseWarning) { 835 function postMessageToIFrame() { 836 var target = settings[id] && settings[id].targetOrigin 837 log( 838 id, 839 '[' + 840 calleeMsg + 841 '] Sending msg to iframe[' + 842 id + 843 '] (' + 844 msg + 845 ') targetOrigin: ' + 846 target 847 ) 848 iframe.contentWindow.postMessage(msgId + msg, target) 849 } 850 851 function iFrameNotFound() { 852 warn(id, '[' + calleeMsg + '] IFrame(' + id + ') not found') 853 } 854 855 function chkAndSend() { 856 if ( 857 iframe && 858 'contentWindow' in iframe && 859 null !== iframe.contentWindow 860 ) { 861 // Null test for PhantomJS 862 postMessageToIFrame() 863 } else { 864 iFrameNotFound() 865 } 866 } 867 868 function warnOnNoResponse() { 869 function warning() {
870 if (settings[id] && !settings[id].loaded && !errorShown) { 871 errorShown = true 872 warn( 873 id, 874 'IFrame has not responded within ' + 875 settings[id].warningTimeout / 1000 + 876 ' seconds. Check iFrameResizer.contentWindow.js has been loaded in iFrame. This message can be ignored if everything is working, or you can set the warningTimeout option to a higher value or zero to suppress this warning.' 877 ) 878 } 879 } 880 881 if ( 882 !!noResponseWarning && 883 settings[id] && 884 !!settings[id].warningTimeout 885 ) { 886 settings[id].msgTimeout = setTimeout( 887 warning, 888 settings[id].warningTimeout 889 ) 890 } 891 } 892 893 var errorShown = false 894 895 id = id || iframe.id 896 897 if (settings[id]) { 898 chkAndSend() 899 warnOnNoResponse() 900 } 901 } 902 903 function createOutgoingMsg(iframeId) { 904 return ( 905 iframeId + 906 ':' + 907 settings[iframeId].bodyMarginV1 + 908 ':' + 909 settings[iframeId].sizeWidth + 910 ':' + 911 settings[iframeId].log + 912 ':' + 913 settings[iframeId].interval + 914 ':' + 915 settings[iframeId].enablePublicMethods + 916 ':' + 917 settings[iframeId].autoResize + 918 ':' + 919 settings[iframeId].bodyMargin + 920 ':' + 921 settings[iframeId].heightCalculationMethod + 922 ':' + 923 settings[iframeId].bodyBackground + 924 ':' + 925 settings[iframeId].bodyPadding + 926 ':' + 927 settings[iframeId].tolerance + 928 ':' + 929 settings[iframeId].inPageLinks + 930 ':' + 931 settings[iframeId].resizeFrom + 932 ':' + 933 settings[iframeId].widthCalculationMethod + 934 ':' + 935 settings[iframeId].mouseEvents 936 ) 937 } 938 939 function isNumber(value) { 940 return typeof value === 'number' 941 } 942 943 function setupIFrame(iframe, options) { 944 function setLimits() { 945 function addStyle(style) { 946 var styleValue = settings[iframeId][style] 947 if (Infinity !== styleValue && 0 !== styleValue) { 948 iframe.style[style] = isNumber(styleValue) 949 ? styleValue + 'px' 950 : styleValue 951 log(iframeId, 'Set ' + style + ' = ' + iframe.style[style]) 952 } 953 } 954 955 function chkMinMax(dimension) { 956 if ( 957 settings[iframeId]['min' + dimension] > 958 settings[iframeId]['max' + dimension] 959 ) { 960 throw new Error( 961 'Value for min' + 962 dimension + 963 ' can not be greater than max' + 964 dimension 965 ) 966 } 967 } 968 969 chkMinMax('Height') 970 chkMinMax('Width') 971 972 addStyle('maxHeight') 973 addStyle('minHeight') 974 addStyle('maxWidth') 975 addStyle('minWidth') 976 } 977 978 function newId() { 979 var id = (options && options.id) || defaults.id + count++ 980 if (null !== document.getElementById(id)) { 981 id += count++ 982 } 983 return id 984 } 985 986 function ensureHasId(iframeId) { 987 if ('' === iframeId) { 988 // eslint-disable-next-line no-multi-assign 989 iframe.id = iframeId = newId() 990 logEnabled = (options || {}).log 991 log( 992 iframeId, 993 'Added missing iframe ID: ' + iframeId + ' (' + iframe.src + ')' 994 ) 995 } 996 997 return iframeId 998 } 999 1000 function setScrolling() { 1001 log( 1002 iframeId, 1003 'IFrame scrolling ' + 1004 (settings[iframeId] && settings[iframeId].scrolling 1005 ? 'enabled' 1006 : 'disabled') + 1007 ' for ' + 1008 iframeId 1009 ) 1010 iframe.style.overflow = 1011 false === (settings[iframeId] && settings[iframeId].scrolling) 1012 ? 'hidden' 1013 : 'auto' 1014 switch (settings[iframeId] && settings[iframeId].scrolling) { 1015 case 'omit': 1016 break 1017 1018 case true: 1019 iframe.scrolling = 'yes' 1020 break 1021 1022 case false: 1023 iframe.scrolling = 'no' 1024 break 1025 1026 default: 1027 iframe.scrolling = settings[iframeId] 1028 ? settings[iframeId].scrolling 1029 : 'no' 1030 } 1031 } 1032 1033 // The V1 iFrame script expects an int, where as in V2 expects a CSS 1034 // string value such as '1px 3em', so if we have an int for V2, set V1=V2 1035 // and then convert V2 to a string PX value. 1036 function setupBodyMarginValues() { 1037 if ( 1038 'number' ===
1039 typeof (settings[iframeId] && settings[iframeId].bodyMargin) || 1040 '0' === (settings[iframeId] && settings[iframeId].bodyMargin) 1041 ) { 1042 settings[iframeId].bodyMarginV1 = settings[iframeId].bodyMargin 1043 settings[iframeId].bodyMargin = 1044 '' + settings[iframeId].bodyMargin + 'px' 1045 } 1046 } 1047 1048 function checkReset() { 1049 // Reduce scope of firstRun to function, because IE8's JS execution 1050 // context stack is borked and this value gets externally 1051 // changed midway through running this function!!! 1052 var firstRun = settings[iframeId] && settings[iframeId].firstRun, 1053 resetRequertMethod = 1054 settings[iframeId] && 1055 settings[iframeId].heightCalculationMethod in resetRequiredMethods 1056 1057 if (!firstRun && resetRequertMethod) { 1058 resetIFrame({ iframe: iframe, height: 0, width: 0, type: 'init' }) 1059 } 1060 } 1061 1062 function setupIFrameObject() { 1063 if (settings[iframeId]) { 1064 settings[iframeId].iframe.iFrameResizer = { 1065 close: closeIFrame.bind(null, settings[iframeId].iframe), 1066 1067 removeListeners: removeIframeListeners.bind( 1068 null, 1069 settings[iframeId].iframe 1070 ), 1071 1072 resize: trigger.bind( 1073 null, 1074 'Window resize', 1075 'resize', 1076 settings[iframeId].iframe 1077 ), 1078 1079 moveToAnchor: function (anchor) { 1080 trigger( 1081 'Move to anchor', 1082 'moveToAnchor:' + anchor, 1083 settings[iframeId].iframe, 1084 iframeId 1085 ) 1086 }, 1087 1088 sendMessage: function (message) { 1089 message = JSON.stringify(message) 1090 trigger( 1091 'Send Message', 1092 'message:' + message, 1093 settings[iframeId].iframe, 1094 iframeId 1095 ) 1096 } 1097 } 1098 } 1099 } 1100 1101 // We have to call trigger twice, as we can not be sure if all 1102 // iframes have completed loading when this code runs. The 1103 // event listener also catches the page changing in the iFrame. 1104 function init(msg) { 1105 function iFrameLoaded() { 1106 trigger('iFrame.onload', msg, iframe, undefined, true) 1107 checkReset() 1108 } 1109 1110 function createDestroyObserver(MutationObserver) { 1111 if (!iframe.parentNode) { 1112 return 1113 } 1114 1115 var destroyObserver = new MutationObserver(function (mutations) { 1116 mutations.forEach(function (mutation) { 1117 var removedNodes = Array.prototype.slice.call(mutation.removedNodes) // Transform NodeList into an Array 1118 removedNodes.forEach(function (removedNode) { 1119 if (removedNode === iframe) { 1120 closeIFrame(iframe) 1121 } 1122 }) 1123 }) 1124 }) 1125 destroyObserver.observe(iframe.parentNode, { 1126 childList: true 1127 }) 1128 } 1129 1130 var MutationObserver = getMutationObserver() 1131 if (MutationObserver) { 1132 createDestroyObserver(MutationObserver) 1133 } 1134 1135 addEventListener(iframe, 'load', iFrameLoaded) 1136 trigger('init', msg, iframe, undefined, true) 1137 } 1138 1139 function checkOptions(options) { 1140 if ('object' !== typeof options) { 1141 throw new TypeError('Options is not an object') 1142 } 1143 } 1144 1145 function copyOptions(options) { 1146 // eslint-disable-next-line no-restricted-syntax 1147 for (var option in defaults) { 1148 if (Object.prototype.hasOwnProperty.call(defaults, option)) { 1149 settings[iframeId][option] = Object.prototype.hasOwnProperty.call( 1150 options, 1151 option 1152 ) 1153 ? options[option] 1154 : defaults[option] 1155 } 1156 } 1157 } 1158 1159 function getTargetOrigin(remoteHost) { 1160 return '' === remoteHost || 1161 null !== remoteHost.match(/^(about:blank|javascript:|file:\/\/)/) 1162 ? '*' 1163 : remoteHost 1164 } 1165 1166 function depricate(key) { 1167 var splitName = key.split('Callback') 1168 1169 if (splitName.length === 2) { 1170 var name = 1171 'on' + splitName[0].charAt(0).toUpperCase() + splitName[0].slice(1) 1172 this[name] = this[key] 1173 delete this[key] 1174 warn( 1175 iframeId, 1176 "Deprecated: '" + 1177 key + 1178 "' has been renamed '" + 1179 name + 1180 "'. The old method will be removed in the next major version." 1181 ) 1182 } 1183 } 1184 1185 function processOptions(options) { 1186 options = options || {} 1187 settings[iframeId] = { 1188 firstRun: true, 1189 iframe: iframe,
1190 remoteHost: iframe.src && iframe.src.split('/').slice(0, 3).join('/') 1191 } 1192 1193 checkOptions(options) 1194 Object.keys(options).forEach(depricate, options) 1195 copyOptions(options) 1196 1197 if (settings[iframeId]) { 1198 settings[iframeId].targetOrigin = 1199 true === settings[iframeId].checkOrigin 1200 ? getTargetOrigin(settings[iframeId].remoteHost) 1201 : '*' 1202 } 1203 } 1204 1205 function beenHere() { 1206 return iframeId in settings && 'iFrameResizer' in iframe 1207 } 1208 1209 var iframeId = ensureHasId(iframe.id) 1210 1211 if (!beenHere()) { 1212 processOptions(options) 1213 setScrolling() 1214 setLimits() 1215 setupBodyMarginValues() 1216 init(createOutgoingMsg(iframeId)) 1217 setupIFrameObject() 1218 } else { 1219 warn(iframeId, 'Ignored iFrame, already setup.') 1220 } 1221 } 1222 1223 function debouce(fn, time) { 1224 if (null === timer) { 1225 timer = setTimeout(function () { 1226 timer = null 1227 fn() 1228 }, time) 1229 } 1230 } 1231 1232 var frameTimer = {} 1233 function debounceFrameEvents(fn, time, frameId) { 1234 if (!frameTimer[frameId]) { 1235 frameTimer[frameId] = setTimeout(function () { 1236 frameTimer[frameId] = null 1237 fn() 1238 }, time) 1239 } 1240 } 1241 1242 // Not testable in PhantomJS 1243 /* istanbul ignore next */ 1244 1245 function fixHiddenIFrames() { 1246 function checkIFrames() { 1247 function checkIFrame(settingId) { 1248 function chkDimension(dimension) { 1249 return ( 1250 '0px' === 1251 (settings[settingId] && settings[settingId].iframe.style[dimension]) 1252 ) 1253 } 1254 1255 function isVisible(el) { 1256 return null !== el.offsetParent 1257 } 1258 1259 if ( 1260 settings[settingId] && 1261 isVisible(settings[settingId].iframe) && 1262 (chkDimension('height') || chkDimension('width')) 1263 ) { 1264 trigger( 1265 'Visibility change', 1266 'resize', 1267 settings[settingId].iframe, 1268 settingId 1269 ) 1270 } 1271 } 1272 1273 Object.keys(settings).forEach(function (key) { 1274 checkIFrame(key) 1275 }) 1276 } 1277 1278 function mutationObserved(mutations) { 1279 log( 1280 'window', 1281 'Mutation observed: ' + mutations[0].target + ' ' + mutations[0].type 1282 ) 1283 debouce(checkIFrames, 16) 1284 } 1285 1286 function createMutationObserver() { 1287 var target = document.querySelector('body'), 1288 config = { 1289 attributes: true, 1290 attributeOldValue: false, 1291 characterData: true, 1292 characterDataOldValue: false, 1293 childList: true, 1294 subtree: true 1295 }, 1296 observer = new MutationObserver(mutationObserved) 1297 1298 observer.observe(target, config) 1299 } 1300 1301 var MutationObserver = getMutationObserver() 1302 if (MutationObserver) { 1303 createMutationObserver() 1304 } 1305 } 1306 1307 function resizeIFrames(event) { 1308 function resize() { 1309 sendTriggerMsg('Window ' + event, 'resize') 1310 } 1311 1312 log('window', 'Trigger event: ' + event) 1313 debouce(resize, 16) 1314 } 1315 1316 // Not testable in PhantomJS 1317 /* istanbul ignore next */ 1318 function tabVisible() { 1319 function resize() { 1320 sendTriggerMsg('Tab Visible', 'resize') 1321 } 1322 1323 if ('hidden' !== document.visibilityState) { 1324 log('document', 'Trigger event: Visibility change') 1325 debouce(resize, 16) 1326 } 1327 } 1328 1329 function sendTriggerMsg(eventName, event) { 1330 function isIFrameResizeEnabled(iframeId) { 1331 return ( 1332 settings[iframeId] && 1333 'parent' === settings[iframeId].resizeFrom && 1334 settings[iframeId].autoResize && 1335 !settings[iframeId].firstRun 1336 ) 1337 } 1338 1339 Object.keys(settings).forEach(function (iframeId) { 1340 if (isIFrameResizeEnabled(iframeId)) { 1341 trigger(eventName, event, settings[iframeId].iframe, iframeId) 1342 } 1343 }) 1344 } 1345 1346 function setupEventListeners() { 1347 addEventListener(window, 'message', iFrameListener) 1348 1349 addEventListener(window, 'resize', function () { 1350 resizeIFrames('resize') 1351 }) 1352 1353 addEventListener(document, 'visibilitychange', tabVisible) 1354 1355 addEventListener(document, '-webkit-visibilitychange', tabVisible) 1356 } 1357 1358 function factory() { 1359 function init(options, element) { 1360 function chkType() { 1361 if (!element.tagName) { 1362 throw new TypeError('Object is not a valid DOM element') 1363 } else if ('IFRAME' !== element.tagName.toUpperCase()) { 1364 throw new TypeError( 1365 'Expected <IFRAME> tag, found <' + element.tagName + '>' 1366 ) 1367 } 1368 } 1369 1370 if (element) { 1371 chkType() 1372 setupIFrame(element, options) 1373 iFrames.push(element) 1374 } 1375 } 1376 1377 function warnDeprecatedOptions(options) { 1378 if (options && options.enablePublicMethods) { 1379 warn( 1380 'enablePublicMethods option has been removed, public methods are now always available in the iFrame' 1381 ) 1382 } 1383 } 1384 1385 var iFrames 1386 1387 setupRequestAnimationFrame() 1388 setupEventListeners() 1389 1390 return function iFrameResizeF(options, target) { 1391 iFrames = [] // Only return iFrames past in on this call 1392 1393 warnDeprecatedOptions(options) 1394 1395 switch (typeof target) { 1396 case 'undefined': 1397 case 'string': 1398 Array.prototype.forEach.call( 1399 document.querySelectorAll(target || 'iframe'), 1400 init.bind(undefined, options) 1401 ) 1402 break 1403 1404 case 'object': 1405 init(options, target) 1406 break 1407 1408 default: 1409 throw new TypeError('Unexpected data type (' + typeof target + ')') 1410 } 1411 1412 return iFrames 1413 } 1414 } 1415 1416 function createJQueryPublicMethod($) { 1417 if (!$.fn) { 1418 info('', 'Unable to bind to jQuery, it is not fully loaded.') 1419 } else if (!$.fn.iFrameResize) { 1420 $.fn.iFrameResize = function $iFrameResizeF(options) { 1421 function init(index, element) { 1422 setupIFrame(element, options) 1423 } 1424 1425 return this.filter('iframe').each(init).end() 1426 } 1427 } 1428 } 1429 1430 if (window.jQuery) { 1431 createJQueryPublicMethod(window.jQuery) 1432 } 1433 1434 if (typeof define === 'function' && define.amd) { 1435 define([], factory) 1436 } else if (typeof module === 'object' && typeof module.exports === 'object') { 1437 // Node for browserfy 1438 module.exports = factory() 1439 } 1440 window.iFrameResize = window.iFrameResize || factory() 1441})()
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.