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